zoukankan      html  css  js  c++  java
  • Java并发:Condition接口

    Condition 接口与 Lock 配合实现了等待 / 通知模式,这个和 Object 的监视器方法(wait、notify、notifyAll 等方法)一样,都是实现了等待 / 通知模式,但这两者在使用的方式以及功能特性上还是有差别的。

    Object 的监视器方法与 Condition 接口的对比
    对比项 Object Monitor Methods Condition
    前置条件 获取对象的锁

    调用Lock.lock()获取锁

    调用Lock.newCondition()获取Condition对象

    调用方式 直接调用,如:Object.wait() 直接调用,如:condition.await()
    等待队列个数 一个 多个
    当前线程释放锁并进入等待状态 支持 支持

    当前线程释放锁并进入等待状态,

    在等待状态中不响应中断

    不支持 支持
    当前线程释放锁并进入等待超时状态 支持 支持
    当前现场释放锁并进入等待状态到将来某个时间 不支持 支持
    唤醒等待队列中的一个线程 支持 支持
    唤醒等待队列中的所有线程 支持 支持

    在 Object 监视模型中,一个对象有一个同步队列和一个等待队列,而 AQS 拥有一个同步队列和多个等待队列。

    Object 的监视模型:

    Condition 的同步队列与等待队列:

    Condition 部分方法以及描述:

    方法名称 描述
    void await() throws
    InterruptedException

    当前线程进入等待状态,直到被通知(signal)或中断,

    当前线程才会进入运行状态且从此await方法返回(表明

    该线程已经获取了Condition对象对应的锁),包括:

    其他线程(调用了interrupt()方法中断了当前线程)调用

    该 Condition 的 signal() 或 signalAll()方法,而当前线

    程被选中唤醒

    void awaitUninterruptibly()
    当前线程进入等待状态,知道被通知,对中断不敏感
    long awaitNanos(long nanosTimeout)
    throws InterruptedException

    当前线程进入等待状态直到被通知、中断或者是超时,

    返回值代表剩余的时间,如果在nanosTimeout纳秒

    之前被唤醒,返回值是(nanosTimeout - 实际耗时)。

    若返回的是0或者负数,超时了。

    boolean awaitUntil(Date deadline)

    当前线程进入等待状态直到被通知、中断或者是到某个

    时间。没有到指定时间就被通知,返回true;否则,

    返回false

    void signal()

    唤醒一个等待在Condition上的线程,该线程从等待方法

    返回前必须获得与Condition相关的锁

    void signalAll()

    唤醒所有等待在Condition上的线程,能够从等待方法

    返回的线程必须获得与Condition相关联的锁

    Condition 的实现

    ① 等待队列

    前面的 Condition 的同步队列与等待队列,队列中的每个节点都包含一个线程引用,该线程就是在Condition对象上等待的线程,如果一个线程调用Condition.await()方法,那么该线程将会释放锁、构造成节点加入等待队列进入等待状态。同步队列和等待队列的节点类型都是AQS中的静态内部类Node。Condition是AQS中的静态内部类。

    ② 等待

    调用Condition的await方法,会使当前线程进入等待队列并释放锁,同时线程状态变成等待状态。当从await方法返回时,当前线程一定获取了Condition相关联的锁。

     1     public final void await() throws InterruptedException {
     2             if (Thread.interrupted())
     3                 throw new InterruptedException();
     4             Node node = addConditionWaiter();    // 当前线程加入等待队列
     5             int savedState = fullyRelease(node);  // 释放同步状态,也就是释放锁
     6             int interruptMode = 0;
     7             while (!isOnSyncQueue(node)) {
     8                 LockSupport.park(this);
     9                 if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
    10                     break;
    11             }
    12             if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
    13                 interruptMode = REINTERRUPT;
    14             if (node.nextWaiter != null) // 线程被取消的话要清除节点
    15                 unlinkCancelledWaiters();
    16             if (interruptMode != 0)
    17                 reportInterruptAfterWait(interruptMode);
    18         }

    如果队列的角度来看await()方法,当调用await()方法时,相当于把同步队列的首节点(获取了锁的节点)移动到Condition的等待队列中,实际上是通过addConditionWaiter()方法把当前线程构造成一个新的节点并将其加入等待队列中。

    ③ 通知

    调用该方法的前置条件是要获取了锁,方法首先用isHeldExclusively()检查,接着获取等待队列的首节点,将其移动到同步队列并使用LockSupport工具类唤醒节点中的线程。

    1     public final void signal() {
    2             if (!isHeldExclusively())
    3                 throw new IllegalMonitorStateException();
    4             Node first = firstWaiter;
    5             if (first != null)
    6                 doSignal(first);
    7         }
    1     private void doSignal(Node first) {
    2             do {
    3                 if ( (firstWaiter = first.nextWaiter) == null)
    4                     lastWaiter = null;
    5                 first.nextWaiter = null;
    6             } while (!transferForSignal(first) &&
    7                      (first = firstWaiter) != null);
    8     }
     1     final boolean transferForSignal(Node node) {
     2         /*
     3          * 如果无法更改waitStatus,则该节点会被取消
     4          */
     5         if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
     6             return false;
     7 
     8         /*
     9          * 拼接到队列上并尝试设置前任的waitStatus以指示线程(可能)正在等待。
    10          * 如果取消或尝试设置waitStatus失败,请唤醒以重新同步
    11          * (在这种情况下,waitStatus可能是暂时性且无害的错误)
    12          */
    13         Node p = enq(node);
    14         int ws = p.waitStatus;
    15         if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
    16             LockSupport.unpark(node.thread);
    17         return true;
    18     }

    节点从等待队列移动到同步队列:

  • 相关阅读:
    jQuery+d3绘制流程图OK
    jQuery+d3绘制流程图OK
    史上最全的后端技术大全,你都了解哪些技术呢?
    史上最全的后端技术大全,你都了解哪些技术呢?
    R语言面板数据分析步骤及流程
    R语言面板数据分析步骤及流程
    SQL Server之深入理解STUFF
    1048:有一门课不及格的学生
    1048:有一门课不及格的学生
    1047:判断能否被3,5,7整除
  • 原文地址:https://www.cnblogs.com/magic-sea/p/11594861.html
Copyright © 2011-2022 走看看