AQS之Condition

Condition提供一种方式让当前的线程挂起操作,直到被另外一个线程通知,因为这些线程的共享状态可能被多个线程访问,因此必须要加锁来保护。Condition在使用的时候必须要和锁在一起使用。

Condition增强实现了的Object.wait()方法,可以对同一个对象进行多次等待。

直接看下AQS中Condition对象的实现吧。

源码

1 首先拿一段来实验的代码,了解Condition怎么用

public class LockDemo {
    static ReentrantLock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 8; i++) {
            new Thread(() -> {
                try {
                    lock.lock();
                    System.out.println(Thread.currentThread() + "locked");
                    condition.await();
                    System.out.println(Thread.currentThread() + "unlocked");
                } catch (InterruptedException e) {
                    //e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }).start();
        }
        
        Thread.sleep(2000);
        lock.lock();
        condition.signalAll();
        lock.unlock();
        new CountDownLatch(1).await();

    }
}

2 看下Condition对象总体提供了那些方法

  • await 进入等待状态,直到被signal唤醒,或者被中断。在调用的生活会释放与之相关的锁
  • awaitUninterruptibly 只能被signal唤醒
  • awaitNanos 等待指定的时间,其余同await
  • await(long time, TimeUnit unit) 同上
  • awaitUntil 同上
  • signal 唤醒一个等待的线程
  • signalAll 唤醒所有的等待线程

await方法

1 看下await方法做了那些事情

  • 添加一个Waiter节点,Waiter节点仅仅作用在Condition对象中,与Lock的Node链表不是同一个
  • 释放当前的锁,返回其状态
  • 判断当前的节点是否在同步队列中
  • 对中断的支持,interruptMode
  • 尝试获取锁
  • 对中断标记位进行处理
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与awaitUninterruptibly对比

如果觉得await方法稍微有点不明白,那么看下awaitUninterruptibly,再看await方法。

  • 添加Node.CONDITION节点
  • 释放锁,返回同步器的状态
  • 如果不在同步队列,进行阻塞,响应中断
    • Queue中的Node状态,在signal的时候会被修改


      image-20200218085206524.png

signal方法

  • 判断当前的线程是否持有排它锁
  • 通知第一个节点
    • 内部是Lock.unpark
    • 然后将first指向第一个等待的节点

doSignalAll则是对所有的节点机械能通知

        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);
        }
        
                private void doSignalAll(Node first) {
            lastWaiter = firstWaiter = null;
            do {
                Node next = first.nextWaiter;
                first.nextWaiter = null;
                transferForSignal(first);
                first = next;
            } while (first != null);
        }

最后

相对比较乱,后面还需要再来一次。

整体我觉得代码设计上值得学,如果拿出来问问题,稍微有点不太适合,有几个关键点估计就可以了。

  • CLH,双向链表的操作
  • Node的状态含义
  • 说下对AQS的理解
  • JUC中常见的工具类
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容