AQS中ConditionObject源码分析

简介

Java 中任何对象都可以被synchronized加锁实现线程同步,其原因是Java中任何类都继承了Object类,Object类中实现了线程通讯的方法如:wait(),wait(long timeout),wait(long timeout, int nanos)与notify(),notifyAll(),通过这些方法可以实现线程的阻塞与唤醒,因为AQS队列是Java在代码层面实现的锁因此也提供了线程间通讯的方法,本文将分析AQS队列中ConditionObject内部类以及探索如何实现线程间的通讯

Condition原理分析

在AQS队列中ConditionObject实现了Condition接口该内部类才是具体实现,condition的使用主要通过如下方式创建

Condition con = lock.newCondition();

wait和signal调用方式分别为

con.await();
con.signal();

在ConditionObject内部复用了AQS的Node作为等待队列的基本元素
lock可以创建多个等待队列,这一点与Object只能创建一个等待队列不同
ConditionObject内部成员变量很简单,一个头指针指,一个为指针分别指向登台队列

/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;

本文重点分析await和signal方法

  • await 方法首先会检查当前线程是否被中断过,如果是直接抛出中断异常
  • 向等待队列尾部添加节点
  • 释放当前线程占用的锁同时唤醒同步队列中的节点开始竞争锁
  • 当signal方法被调用后或者产生中断当前线程才会退出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;
    }
    //线程在被signal后需要重新获取到锁并执行完await方法后退出
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
       interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
       unlinkCancelledWaiters();
    if (interruptMode != 0)
       reportInterruptAfterWait(interruptMode);
}

addConditionWaiter方法分析

  • 首先判断队列尾部节点状态如果状态是非condition状态需要清理
  • 将当前线程放入node节点插入队列尾部
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;
 }

private void unlinkCancelledWaiters() {
     Node t = firstWaiter;
     Node trail = null;
     while (t != null) {
         Node next = t.nextWaiter;
         if (t.waitStatus != Node.CONDITION) {
            t.nextWaiter = null;
            if (trail == null)
               firstWaiter = next;
            else
               trail.nextWaiter = next;
            if (next == null)
               lastWaiter = trail;
         }
         else
            trail = t;
         t = next;
     }
 }

isOnSyncQueue判断当前节点是否在同步队列中

  • 首先判断当前节点的状态如果是CONDITION或者prev为null则该节点一定不在同步队列中,如果next不为空则一定在同步队列中
  • 从同步队列尾部开始判断节点是都在队列中
final boolean isOnSyncQueue(Node node) {
    if (node.waitStatus == Node.CONDITION || node.prev == null)
       return false;
    if (node.next != null) // If has successor, it must be on queue
       return true;
   return findNodeFromTail(node);
 }
 private boolean findNodeFromTail(Node node) {
     Node t = tail;
     for (;;) {
         if (t == node)
            return true;
         if (t == null)
            return false;
        t = t.prev;
    }
}

signal方法分析

调用condition的signal或者signalAll方法可以将等待队列中等待时间最长的节点移动到同步队列中,使得该节点能够有机会获得lock。按照等待队列是先进先出(FIFO)的,所以等待队列的头节点必然会是等待时间最长的节点,也就是每次调用condition的signal方法是将头节点移动到同步队列中,代码通过doSignal方法将等待队列中的第一个节点进行释放

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);
}
//1.将头结点的状态更改为CONDITION;
//2.调用enq方法,将该节点尾插入到同步队列
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;
}

  • 参考文章 https://www.jianshu.com/p/28387056eeb4
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值