AQS相关(lock、unlock、await、signal)

节点状态

/** 节点已被取消 */
static final int CANCELLED =  1;
/** 
 *  后继节点的线程处于等待状态,
 *  而当前节点的线程如果释放了同步状态或者被取消,
 *  那么就会通知后继节点,让后继节点的线程能够运行 
 */
static final int SIGNAL    = -1;
/** 
 * 节点在等待队列中,节点线程等待在Condition上,
 * 不过当其他的线程对Condition调用了signal()方法后,
 * 该节点就会从等待队列转移到同步队列中,然后开始尝试对同步状态的获取 
 */
static final int CONDITION = -2;
/**
 * 表示下一次的共享式同步状态获取将会无条件的被传播下去
 */
static final int PROPAGATE = -3;

参考资料

lock 方法流程

AQS相关(lock、unlock、await、signal)

通过流程可以看出,公平与非公平只针对新线程与同步队列中的线程。


源码:

  • 公平锁
       final void lock() {
            // 调用父类的 acquire
            acquire(1);
        }
        // 父类
        public final void acquire(int arg) {
          // 尝试抢锁
          if (!tryAcquire(arg) &&
          // 抢锁失败加入队列排队获取锁,此步骤为自旋阻塞,addWaiter对应流程图2
          acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
          // 如果在队列中抢锁失败,则关闭当前线程
              selfInterrupt();
        }
        // 尝试抢锁
        protected final boolean tryAcquire(int acquires) {
            // 获取当前线程
            final Thread current = Thread.currentThread();
            // 获取当前 state 值
            int c = getState();
            // 如果当前 state 为没人占用状态
            if (c == 0) {
                // 同步队列中是否有抢锁的线程,对应流程图1
                if (!hasQueuedPredecessors() &&
                    // 如果前面没有抢锁的线程,CAS 改写 state
                    compareAndSetState(0, acquires)) {
                    // 如果写成功则将当前锁的持有人标记为当前线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 当前 state 为当前线程占用
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
  • 非公平锁
        final void lock() {
            // CAS 改写 state,写成功代表抢锁成功
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                // 上面写失败了就调用父级的 acquire 方法,同公平锁
                acquire(1);
        }
        protected final boolean tryAcquire(int acquires) {
          return nonfairTryAcquire(acquires);
        }
       final boolean nonfairTryAcquire(int acquires) {
            // 获取当前线程
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                // 比公平锁少一步判断同步队列的步骤
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
  • acquireQueued 队列阻塞抢锁
   // 对应流程3
   final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                // 获取当前结点的上一个节点
                final Node p = node.predecessor();
                // 如果上一个节点为 head,则尝试获取锁
                if (p == head && tryAcquire(arg)) {
                    // 抢锁成功后将 head 设为当前节点
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                // 抢锁失败,判断当前线程是否应该关闭
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            // 抢锁失败,进行取消操作
            if (failed)
                cancelAcquire(node);
        }
    }

wait & signal (等待与唤醒)

阻塞队列中,如果当前队列为空,则会调用 notEmpty.await() 方法,notEmptyConditionObject,通常阻塞队列中会有两个 ConditionObject,一个负责【取(notEmpty)】的线程,一个负责【存(notFull)】的线程。一个 ConditionObject 就是一个等待队列,调用 await() 方法就是将当前线程从同步队列移到等待队列中。

AQS相关(lock、unlock、await、signal)

源码解析:

        public final void await() throws InterruptedException {
            // 如果当前线程是中断状态则抛出中断异常
            if (Thread.interrupted())
                throw new InterruptedException();
            // 将节点加入等待队列,对应流程1
            Node node = addConditionWaiter();
            // 释放当前节点所持的锁,对应流程2
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            // 如果当前节点不在同步队列中,则挂起等待唤醒,对应流程3
            // 当线程被唤醒时,节点会加入同步队列,则会跳出 while,进入下面的逻辑
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            // 进入同步队列抢锁
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }
  • signal
        public final void signal() {
            // 如果不是当前持锁的线程操作,则抛出异常
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                // 如果等待队列有线程在等待
                doSignal(first);
        }
         private void doSignal(Node first) {
            do {
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }
    final boolean transferForSignal(Node node) {
        /*
         * 如果不能修改状态,则表示当前节点已被取消
         */
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

        // 将节点加入同步队列,返回当前节点的上一个节点
        Node p = enq(node);
        // 获取上一个节点的等待状态
        int ws = p.waitStatus;
        // 如果上一个节点为取消
        // 或者上一个节点的状态已经时 SIGNAL 状态
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            // 唤醒当前节点
            LockSupport.unpark(node.thread);
        // 否则就交给同步队列进行唤醒,每个线程释放锁时都会去唤醒他的下一个节点
        return true;
    }
  • realse
    public final boolean release(int arg) {
        // 尝试释放锁
        if (tryRelease(arg)) {
            Node h = head;
            // 释放成功后唤醒下一个节点(如果有的话),对应流程4
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 2
ShiKi

这是转java了?我记得博主以前是phper啊

2年前 评论

@ShiKi :joy:是的,最近在学习Java,语言都是相通的,了解原理其实都差不多

2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!