Conditon源码详解

读写锁的读锁不支持Condition,读写锁的写锁和互斥锁支持Condition。


前言

Condition有两个核心方法await和signal方法,调用时必须获取到Lock锁。

Condition里维护等待队列的是一个单向链表。

阻塞时在等待队列中插入一个condition节点,然后先释放锁再park阻塞。被唤醒时通过查看是否在AQS同步队列中判断是否是被signal唤醒的。是-重新拿锁。

释放时,从等待队列中取头节点,将状态改为初始状态,然后加入到AQS队列中调用unpark唤醒。


一、Condition与Lock的关系

condition本身也是一个接口,其功能和wait/notify类似。

其提供的就两个核心方法,await和signal方法。分别对应着Object的wait和notify方法。调用Object对象的这两个方法,需要在同步代码块里面,即必须先获取到锁才能执行这两个方法。同理,Condition调用这两个方法,也必须先获取到锁。只不过Object的锁是synchronized,而Condition是Lock锁。这里先声明两个概念:

  1. 等待队列:Condition上用来等待这个条件的队列,这是一个单向链表
  2. 同步队列:就是AQS等待拿锁的CLH队列,这是一个双向链表

二、Condition实现原理

Condition是一个接口,AQS抽象类中ConditionObject实现了Condition的全部功能。

每一个Condition对象上面都阻塞了多个线程。因此,在ConditionObject内部也有一个单向链表组成的队列。

await()实现分析

public final void await() throws InterruptedException {
            if (Thread.interrupted()) //执行await操作时,收到中断信号抛出异常
                throw new InterruptedException();
            Node node = addConditionWaiter(); //加入Condition的等待队列
            int savedState = fullyRelease(node); //阻塞在Condition之前必须先释放锁,否则会死锁
            int interruptMode = 0;
            //判断Node是否在AQS队列中,初始只在Condition队列中,在执行notify时会放入AQS同步队列
            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); //被中断唤醒,向外抛出中断异常
        }

线程调用await的时候,肯定已经先拿到了锁。所以在addConditionWaiter内部的操作不需要CAS,线程天生安全。 

private Node addConditionWaiter() {
            Node t = lastWaiter;
            // 如果尾结点被取消,将取消节点释放。
            if (t != null && t.waitStatus != Node.CONDITION) {
                unlinkCancelledWaiters();
                t = lastWaiter;
            }
            //新节点,状态为condition = -2
            Node node = new Node(Thread.currentThread(), Node.CONDITION);
            if (t == null)
                firstWaiter = node;
            else
                t.nextWaiter = node;
            lastWaiter = node;
            return node;
        }
final int fullyRelease(Node node) {
        boolean failed = true;
        try {
            int savedState = getState();
            if (release(savedState)) {
                failed = false;
                return savedState;
            } else {
                throw new IllegalMonitorStateException();
            }
        } finally {
            if (failed)
                node.waitStatus = Node.CANCELLED;
        }
    }

 

final boolean isOnSyncQueue(Node node) {
        //节点状态为condition或者节点的前一个节点为空一定不在AQS
        //加入AQS队列的一定是初始状态,因为是先CAS成初始状态再插入AQS
        //AQS中有一个空的头节点,所以如果在AQS中前节点一定不为空
        if (node.waitStatus == Node.CONDITION || node.prev == null)
            return false;
        if (node.next != null) //节点的后继节点不为null,说明节点肯定在队列中,返回true,这里很重要的一点要明白,prev和next都是针对同步队列的节点
            return true;
        /*
         * 当满足了节点状态不为condition且头节点不为空可能还处于enq函数CAS插入AQS的过程中
         */
        return findNodeFromTail(node);
    }

//是一个兜底的策略,从尾部开始找
private boolean findNodeFromTail(Node node) {
    //取得同步队列的队尾元素
    Node t = tail;
    //无限循环,从队尾元素一直往前找,找到相等的节点就说明节点在队列中,node为null了,说明前面已经没有节点可以找了,那就返回false
    for (;;) {
        if (t == node)
            return true;
        if (t == null)
            return false;
        t = t.prev;
    }
}

在线程执行wait操作之前,必须先释放锁,也就是fullyRelease(node),否则会发生死锁。

初始的时候,Node只在Condition的队列里,而不在AQS的队列里。但执行notify操作的时候,会放进AQS的同步队列。

awaitUninterruptibly()实现分析

awaitUninterruptibly不会响应中断,收到中断信号后不会抛出异常而是继续执行。

public final void awaitUninterruptibly() {

    Node node = addConditionWaiter();

    int savedState = fullyRelease(node);

    boolean interrupted = false;
    
    while (!isOnSyncQueue(node)) {

        LockSupport.park(this);

        if (Thread.interrupted())

            interrupted = true;

        }
    //重新拿锁过程中收到中断信号返回true
    if (acquireQueued(node, savedState) || interrupted)

        selfInterrupt(); //中断补偿机制

}

notify实现分析

public final void signal() {
            if (!isHeldExclusively()) //只有持有锁的线程才能执行signal
                throw new IllegalMonitorStateException();
            Node first = firstWaiter; //唤醒头节点,FIFO
            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) {
        /*
         * node可能正在取消,所以需要CAS,失败说明被取消了
         */
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

        /*
         * 将节点放入AQS同步队列中
         */
        Node p = enq(node);
        int ws = p.waitStatus;
        //唤醒
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }

将状态从condition设置为0,然后先放入AQS中然后再唤醒。所以await中可以通过是否在AQS中来判断是否被unpark唤醒的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值