并发编程的实现原理-Condition-笔记

Condition(java.util.concurrent.locks.Condition)java

  • 任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object上),
    • 主要包括wait()、notify()以及notifyAll()方法,
    • 这些方法与synchronized同步关键字配合,能够实现等待/通知模式
  • JUC包提供了Condition来对锁进行精准控制,
    • Condition是一个多线程协调通讯的工具类,
    • 可让某些线程一块儿等待某个条件(condition),
    • 只有知足条件时,线程才会被唤醒。

condition使用案例node

  • ConditionWait
    • public class ConditionDemoWait implements Runnable{
          private Lock lock;
          private Condition condition;
          public ConditionDemoWait(Lock lock, Condition condition){
              this.lock=lock;
              this.condition=condition;
          }
          @Override
          public void run() {
              System.out.println("begin -ConditionDemoWait");
              try {
                  lock.lock();  ////得到锁
                  condition.await();/////// 这里会添加到condition 队列,后续unlock释放锁,
                                    //////signal 会去condition队列唤醒他,把他放回AQS队列
                  System.out.println("end - ConditionDemoWait");
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }finally {
                  lock.unlock();///释放锁
              }
          }
      }

       

  • ConditionSignal
    • public class ConditionDemoSignal implements Runnable {
          private Lock lock;
          private Condition condition;
      
          public ConditionDemoSignal(Lock lock, Condition condition) {
              this.lock = lock;
              this.condition = condition;
          }
      
          @Override
          public void run() {
              System.out.println("begin -ConditionDemoSignal");
              try {
                  lock.lock();   //////// 获取锁
                  condition.signal();//////// 唤醒ConditionDemoWait 线程,加到AQS队列,使得await 后面代码获得执行
                  System.out.println("end - ConditionDemoSignal");
              } finally {
                  lock.unlock();/////////释放锁
              }
          }
      }

       

  • 经过这个案例简单实现了wait和notify的功能,
    • 当调用await方法后,当前线程会释放锁并等待,
    • 而其余线程调用condition对象的signal或者signalall方法通知并被阻塞的线程,
    • 而后本身执行unlock释放锁,
    • 被唤醒的线程得到以前的锁继续执行,最后释放锁。
  • 因此,condition中两个最重要的方法,一个是await,一个是signal方法
    • await:把当前线程阻塞挂起
    • signal:唤醒阻塞的线程

await方法数据结构

  • 调用Condition的await()方法(或者以await开头的方法),
    • 会使当前线程进入等待队列
    • 并释放锁,
    • 同时线程状态变为等待状态。
  • 当从await()方法返回时,
    • 当前线程必定获取了Condition相关联的锁
public final void await() throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        Node node = addConditionWaiter(); //建立一个新的节点,节点状态为condition,采用的数据结构仍然是链表
        int savedState = fullyRelease(node); //释放当前的锁,获得锁的状态,并唤醒AQS队列中的一个线程
        int interruptMode = 0;
//若是当前节点没有在同步队列上,即尚未被signal,则将当前线程阻塞
//isOnSyncQueue 判断当前 node 状态,
// 若是是 CONDITION 状态,或者不在队列上了,就继续阻塞,
// 还在队列上且不是 CONDITION 状态了,就结束循环和阻塞
        while (!isOnSyncQueue(node)) {//第一次判断的是false,由于前面已经释放锁了
            LockSupport.park(this); // 第一次老是 park 本身,开始阻塞等待
// 线程判断本身在等待过程当中是否被中断了,
//若是没有中断,则再次循环,会在 isOnSyncQueue 中判断本身是否在队列上.
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
        }
// 当这个线程醒来,会尝试拿锁, 当 acquireQueued 返回 false 就是拿到锁了.
// interruptMode != THROW_IE -> 表示这个线程没有成功将 node 入队,但 signal 执行了 enq 方法让其入队了.
// 将这个变量设置成 REINTERRUPT.
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
// 若是 node 的下一个等待者不是 null, 则进行清理,清理 Condition 队列上的节点.
        // 若是是 null ,就没有什么好清理的了.
        if (node.nextWaiter != null) // clean up if cancelled
            unlinkCancelledWaiters();
// 若是线程被中断了,须要抛出异常.或者什么都不作
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
    }

signal多线程

  • 调用Condition的signal()方法,
    • 将会唤醒在等待队列中等待时间最长的节点(首节点),
    • 在唤醒节点以前,会将节点移到同步队列中
public final void signal() {
        if (!isHeldExclusively()) //先判断当前线程是否得到了锁
            throw new IllegalMonitorStateException();
        Node first = firstWaiter; // 拿到 Condition 队列上第一个节点
        if (first != null)
            doSignal(first);
    }
/** ######################################### */
    private void doSignal(Node first) {
        do {
            if ( (firstWaiter = first.nextWaiter) == null)// 若是第一个节点的下一个节点是 null,
                                                          //那么, 最后一个节点也是 null.
                    lastWaiter = null; // 将 next 节点设置成 null
            first.nextWaiter = null;
        } while (!transferForSignal(first) &&
                (first = firstWaiter) != null);
    }
  • 该方法先是 CAS 修改了节点状态,
    • 若是成功,就将这个节点放到 AQS 队列中,
    • 而后唤醒这个节点上的线程。
    • 此时,那个节点就会在 await 方法中苏醒
final boolean transferForSignal(Node node) {
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;
        Node p = enq(node);
        int ws = p.waitStatus;
        // 若是上一个节点的状态被取消了, 
        // 或者尝试设置上一个节点的状态为 SIGNAL 失败了(SIGNAL 表示: 他的
        next 节点须要中止阻塞),
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread); // 唤醒输入节点上的线程.
        return true;
    }

我的理解:ide

  • 一共是俩队列,AQS队列、Condition队列
相关文章
相关标签/搜索