每期总结一个小的知识点和相干面试题,嘿嘿,又来和大家独特学习了。

GUC中有个类咱们用的比拟少,然而他确是很多类中不可或缺的成员。他就是Condition。

从字面意思了解就是条件,那条件的话就有true or false。那Condition是起到一个
多线程共享标识位执行阻塞的作用,true 的时候通过, false 的时候期待。

1、Condition的应用

通过上面的一个代码能够看进去如何应用它。

// thread 1System.out.println("1 am thread 1 start");condition.await();//阻塞System.out.println("1 am thread 1 end");// thread 2System.out.println("1 am thread 2");condition.signal()://唤醒

假如线程1和线程2,并发执行。那么执行的后输入会是:

1 am thread 1 start1 am thread 21 am thread 1 end

发现没有,是不是和一个Object对象的wait(),notify()很像。惟一的区别是Condition不须要先
synchronize润饰后能力调用阻塞办法。那是不是应用起来更不便了。像阻塞队列外面empty和full的判断
都是基于Condition来实现的,能够保障告诉程序。

2、Condition的原理

一个Condition实例实质上绑定到一个锁。 要取得特定Condition实例的Condition实例,请应用其newCondition()办法。

   final Lock lock = new ReentrantLock();   // 须要绑定到lock   final Condition notFull  = lock.newCondition();    final Condition notEmpty = lock.newCondition(); 

2.1 Condition API

Modifier and TypeMethod and Description
voidawait()导致以后线程等到发信号或 interrupted 。
booleanawait(long time, TimeUnit unit)使以后线程期待直到发出信号或中断,或指定的等待时间过来。
longawaitNanos(long nanosTimeout)使以后线程期待直到发出信号或中断,或指定的等待时间过来。
voidawaitUninterruptibly()使以后线程期待直到发出信号。
booleanawaitUntil(Date deadline)使以后线程期待直到发出信号或中断,或者指定的最初期限过来。
voidsignal()唤醒一个期待线程。
voidsignalAll()唤醒所有期待线程。

2.1 Condition实现

初始化办法:

final ConditionObject newCondition() {  // ConditionObject是AQS的外部类,外部类当中能够调用外部类当中的属性和办法  return new ConditionObject();}

首先看下await办法,如何实现阻塞期待:

public final void await() throws InterruptedException {    // 如果以后线程被中断,则抛出 InterruptedException    if (Thread.interrupted())        throw new InterruptedException();    // 增加一个期待node,能够看进去Condition就是对AQS的node节点的各种判断    Node node = addConditionWaiter();    // 用node以后状态值调用开释;返回保留状态    int savedState = fullyRelease(node);    int interruptMode = 0;    // 是在同步队列?isOnSyncQueue在Node的next不为空是返回true,什么意思就是非第一个LCH节点就会执行线程阻塞。    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();    // 0是默认状态    if (interruptMode != 0)        // interrupt解决        reportInterruptAfterWait(interruptMode);}private Node addConditionWaiter() {    Node t = lastWaiter;    // If lastWaiter is cancelled, clean out.    if (t != null && t.waitStatus != Node.CONDITION) {        unlinkCancelledWaiters();        t = lastWaiter;    }    // 创立一个condition状态的Node    Node node = new Node(Thread.currentThread(), Node.CONDITION);    if (t == null)        firstWaiter = node;    else        t.nextWaiter = node;    lastWaiter = node;    return node;}final int fullyRelease(Node node) {    boolean failed = true;    try {        int savedState = getState();        if (release(savedState)) {            failed = false;            return savedState;        } else {            throw new IllegalMonitorStateException();        }    } finally {        if (failed)            node.waitStatus = Node.CANCELLED;    }}

那么再看下signal如何实现唤醒Node:

public final void signal() {    // 判断是否有线程执行权限,lock调用线程才有权限,getExclusiveOwnerThread() == Thread.currentThread();    if (!isHeldExclusively())        throw new IllegalMonitorStateException();    Node first = firstWaiter;    // 存在期待的node才须要唤醒    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);}final boolean transferForSignal(Node node) {    /*     * If cannot change waitStatus, the node has been cancelled.     */    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))        return false;    /*     * Splice onto queue and try to set waitStatus of predecessor to     * indicate that thread is (probably) waiting. If cancelled or     * attempt to set waitStatus fails, wake up to resync (in which     * case the waitStatus can be transiently and harmlessly wrong).     */    Node p = enq(node);    int ws = p.waitStatus;    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))        // unpark唤醒线程        LockSupport.unpark(node.thread);    return true;}

3、Condition相干面试题

3.1、什么是Java虚伪唤醒及如何防止虚伪唤醒?

虚伪唤醒

当一个条件满足时,很多线程都被唤醒了,然而只有其中局部是有用的唤醒,其它的唤醒都是无用功

比如说买货,如果商品原本没有货物,忽然进了一件商品,这是所有的线程都被唤醒了,然而只能一个人买,所以其他人都是假唤醒,获取不到对象的锁

如何防止虚伪唤醒

所有的线程都被唤醒了的时候,判断临界条件应用while判断,这样在被唤醒的时候,能够再check一次条件。

3.2、Mutex、BooleanLatch 什么场景应用

Mutex:这是一个不可重入互斥锁类,它应用零值来示意解锁状态,一个示意锁定状态。 尽管不可重入锁不严格要求记录以后的所有者线程,然而这样做无论如何使得应用更容易监督。 它还反对条件并公开其中一种仪器办法

BooleanLatch:这是一个相似CountDownLatch的闩锁类,只是它只须要一个signal能力触发

3.3、CLH锁和MCS锁的差别

  • 从代码实现来看,CLH比MCS要简略得多。
  • 从自旋的条件来看,CLH是在前驱节点的属性上自旋,而MCS是在本地属性变量上自旋。
  • 从链表队列来看,CLHNode不间接持有前驱节点,CLH锁开释时只须要扭转本人的属性;MCSNode间接持有后继节点,MCS锁开释须要扭转后继节点的属性。
  • CLH锁开释时只须要扭转本人的属性,MCS锁开释则须要扭转后继节点的属性

3.4、Node的状态有哪些

  • CANCELLED(1):示意以后结点已勾销调度。当timeout或被中断(响应中断的状况下),会触发变更为此状态,进入该状态后的结点将不会再变动。
  • SIGNAL(-1):示意后继结点在期待以后结点唤醒。后继结点入队时,会将前继结点的状态更新为SIGNAL。
  • CONDITION(-2):示意结点期待在Condition上,当其余线程调用了Condition的signal()办法后,CONDITION状态的结点将从期待队列转移到同步队列中,期待获取同步锁。
  • PROPAGATE(-3):共享模式下,前继结点不仅会唤醒其后继结点,同时也可能会唤醒后继的后继结点。
  • 0:新结点入队时的默认状态。
本文由猿必过 YBG 公布