java并发---Condition简单分析

本文详细解析了Condition接口及其默认实现ConditionObject,阐述了等待/通知功能的实现原理,包括await、signal和signalAll方法的工作流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Condition的概念

Condition是一个接口,默认实现是Lock里的ConditionObject。创建方式一般是lock.newCondition();主要是用来实现等待/通知功能的。

Condition的源码简单分析

Condition接口定义

public interface Condition {

    void await() throws InterruptedException;

   
    void awaitUninterruptibly();

    long awaitNanos(long nanosTimeout) throws InterruptedException;

   
    boolean await(long time, TimeUnit unit) throws InterruptedException;

   
    boolean awaitUntil(Date deadline) throws InterruptedException;

  
    void signal();

  
    void signalAll();
}

主要就是等待/通知方法。

在看Condition的实现类ConditionObject。ConditionObject实现了Condition接口,和Serializable。

public class ConditionObject implements Condition, java.io.Serializable {
        private static final long serialVersionUID = 1173984872572414699L;
        /** First node of condition queue. */
        private transient Node firstWaiter;
        /** Last node of condition queue. */
        private transient Node lastWaiter;

从属性可以看到,和其他的同步队列一样,使用的都是AQS的节点Node类。并维护了首节点和尾节点。

await()

     public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            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的方法流程

  1. 当前线程是否被中断,中断的话就抛出异常。
  2. 把当前线程创建为node,放入等待队列。
  3. 把当前线程占有的同步锁释放。
  4. 循环判断当前线程是否已经放入队列,已经加入了,就直接退出,不然就阻塞,循环判断。
  5. 调用 acquireQueued() 方法加入同步状态竞争

下面看看是如何把当前线程创建为Node的

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)
                firstWaiter = node;
            else
                t.nextWaiter = node;
            lastWaiter = node;
            return node;
        }
  1. 清除所有状态不为CONDITION的节点
  2. 创建node实例,把新创建的node放在队列的尾部

通知

signal()

    public final void signal() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignal(first);
        }
  1. 判断当前线程是否拥有锁
  2. 对首节点做doSignal处理。。

因为condition的增加操作是放在队尾的,唤醒时是先对首节点唤醒,所以是FIFO。

doSignal

      private void doSignal(Node first) {
            do {
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }

方法很简单,主要看transferForSignal,transferForSignal是把节点移动到同步队列

transferForSignal

    final boolean transferForSignal(Node node) {
       
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

       
        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }
  1. 把节点的状态设为0,失败就直接返回false
  2. 调用enq方法,将节点设置到同步队列里
  3. 如果节点的状态为CANCELLED或者修改waitStatus失败,就直接唤醒  

signalAll()

    public final void signalAll() {
            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);
        }

大家可以看到,singalAll方法很简单,主要就是从头节点开始,把所有节点都移动到同步队列里,并把每个线程都唤醒。

 

下面来总结一下COndition的工作流程吧

  1. 获取到锁的线程,因为什么东西还未满足继续执行的条件,就调用Condition.await()方法,await会将当前线程加入到等待队列里,然后一直在await的while循环里等待当前节点是否已经被放在同步队列里,在同步队列里的话就尝试去获取锁,否则就一直阻塞。
  2. 当其他的线程调用singal()方法时,首先判断是否已经获取了锁没,然后调用doSignal方法把头节点放入同步队列里,并使用 LockSupport.unpark唤醒其中的线程。
  3. 被唤醒的线程会从await()方法里的while循环里推出来,然后调用acquireQueued去竞争锁,竞争成功就会推出await方法,线程继续网下执行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值