源码解析 Lock Condition

接上一篇:源码解析 ReentrantLock

1. await()

查看 ReentrantLock.newCondition() 的 await() 方法

void await() throws InterruptedException;

这是一个接口方法,实现者是 AbstractQueuedSynchronizer 的 await()

    public final void await() throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        //往条件等待队列中,插入一个条件等待节点(内含当前线程)
        Node node = addConditionWaiter();
        //尝试释放锁,返回值:表示之前被加锁次数(可重入锁可以被多次加锁),一般为 1
        int savedState = fullyRelease(node);
        int interruptMode = 0;
        //判断是否位于同步队列中,如果不是,不断挂起线程,直到进入同步队列
        while (!isOnSyncQueue(node)) {
            //使用当前 AbstractQueuedSynchronizer 对象,挂起当前线程
            LockSupport.park(this);
            //在条件等待过程中,判断是否线程中断,是的话跳出循环,结束等待
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;

            //checkInterruptWhileWaiting(node) 很有趣:如果当前线程已中断,判断
            //(1) 位于 signal 释放条件信号之前,将当前节点从条件等待队列移入同步队列
            //(2) 位于 signal 释放条件信号之后,如果同步队列中不存在当前节点(signal() 方法不仅能改变条件等待状态,同时还会负责将节点移入同步队列),
            //呢么将永远让线程调度器无视掉当前线程
        }
        //acquireQueued 进入队列无限等待,直到获得锁,如果返回 true,表示线程被中断了
        //具体注释在:《源码解析 ReentrantLock》
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        //如果存在后继条件等待节点,尝试清除所有 “取消状态” 的节点
        //这里的策略是从第一个条件等待节点开始,向后遍历,依次清除 “取消状态” 的节点,具体请看源码
        if (node.nextWaiter != null) // clean up if cancelled
            unlinkCancelledWaiters();
        //判断当前线程是否被中断
        if (interruptMode != 0)
            //根据情况,选择 (1)抛出中断异常 (2)手动中断当前线程
            reportInterruptAfterWait(interruptMode);

        //如果到达此处,表示满足条件,业务代码继续向下执行
    }

查看 addConditionWaiter(),了解如何将线程插入条件等待队列

    private Node addConditionWaiter() {
        Node t = lastWaiter;
        // If lastWaiter is cancelled, clean out.
        if (t != null && t.waitStatus != Node.CONDITION) {
            //如果最后一个条件等待节点已经被取消了,先清除它
            //这里的策略是从第一个条件等待节点开始,向后遍历,依次清除 “取消状态” 的节点,具体请看源码
            unlinkCancelledWaiters();
            //在执行清除后,重新获取最后一个条件等待节点
            t = lastWaiter;
        }
        Node node = new Node(Thread.currentThread(), Node.CONDITION);
        if (t == null)
            //t 为 null,表示队列为空,于是设置头结点,初始化队列
            firstWaiter = node;
        else
            t.nextWaiter = node;
        lastWaiter = node;
        return node;
    }

查看 fullyRelease(node),了解当条件不满足时,释放锁的工作内容

    final int fullyRelease(Node node) {
        //失败标志,默认为 true
        boolean failed = true;
        try {
            //获取同步状态值,>0 表示锁资源被占用,=N 表示被加锁 N 次
            int savedState = getState();
            //尝试释放锁
            if (release(savedState)) {
                //释放锁成功
                failed = false;
                return savedState;
            } else {
                //释放锁失败,抛错,因为释放锁的方法里还包括了唤醒挂起线程,如果失败了就全线程阻塞了
                throw new IllegalMonitorStateException();
            }
        } finally {
            if (failed)
                //如果整个流程失败了,将当前节点设置为 “取消状态”
                node.waitStatus = Node.CANCELLED;
        }
    }

    //......

    public final boolean release(int arg) {
        //尝试释放锁,具体代码在下边
        if (tryRelease(arg)) {
            //到达此处表示,释放锁成功
            Node h = head;
            //如果 h 有效,唤醒 h 的有效后继者(即 “非取消状态” 的后继节点)
            //可以简单理解为:队列先进先出,锁(系统)资源分配给下一个线程节点
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

    /*注意:这是 ReentrantLlock.Sync.tryRelease() 方法,与上边方法不在同一个类 */
    protected final boolean tryRelease(int releases) {
        //将同步状态-1(如果同步状态=3,表示由同一线程加锁 3 次)
        int c = getState() - releases;
        //如果当前线程不是 “独占线程”,抛出错误
        //举个例子:如果一个线程明明没有获得锁,却尝试释放锁,就会触发该异常
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        //表示是否应该释放锁,因为有时候同一个锁可能被加锁多次,一次释放是不够的
        boolean free = false;
        //同步状态=0,表示无任何线程占有锁,锁(系统)资源是自由的
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }

2. signalAll()

查看 ReentrantLock.newCondition() 的 signalAll() 方法

    public final void signalAll() {
        //如果当前线程不是 “独占线程”,抛出错误
        //举个例子:如果一个线程明明没有获得锁,却尝试释放锁,就会触发该异常
        //感觉此处别有用意,个人猜测:强制开发者将 signalAll() 放在 unlock() 代码之前
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        //获取第一个条件等待节点
        Node first = firstWaiter;
        if (first != null)
            doSignalAll(first);
    }

    //...

    private void doSignalAll(Node first) {
        //删除条件等待队列
        lastWaiter = firstWaiter = null;
        do {
            //遍历整个条件等待队列
            Node next = first.nextWaiter;
            first.nextWaiter = null;
            //发送一个信号,将其从条件等待队列移入同步队列
            //如此一来,该线程就脱离了条件等待,进入同步队列去获取锁
            transferForSignal(first);
            first = next;
        } while (first != null);
    }


    //...


    final boolean transferForSignal(Node node) {
        /*
         * If cannot change waitStatus, the node has been cancelled.
         */
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

        //移入同步队列
        /*
         * Splice onto queue and try to set waitStatus of predecessor to
         * indicate that thread is (probably) waiting. If cancelled or
         * attempt to set waitStatus fails, wake up to resync (in which
         * case the waitStatus can be transiently and harmlessly wrong).
         */
        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值