synchronized notify
lock/Condition 实现类似 synchronized与notify/notifyall wait
Condition 示例新建3个类WaitCondition、NotifyCondition、Test
package condition;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
等待条件执行
* @author sun
* @date 2019/8/19
*/
public class WaitCondition implements Runnable{
private Lock lock;
private Condition condition;
public WaitCondition(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
try{
lock.lock();
System.out.println("begin wait");
condition.await();
System.out.println("end wait");
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
package condition;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
唤醒等待的线程
* @author sun
* @date 2019/8/19
*/
public class NotifyCondition implements Runnable {
private Lock lock;
private Condition condition;
public NotifyCondition(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
try{
lock.lock();
System.out.println("begin notify");
condition.signal();
System.out.println("end notify");
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
package condition;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
测试
* @author sun
* @date 2019/8/19
*/
public class Test {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(new WaitCondition(lock,condition)).start();
new Thread(new NotifyCondition(lock,condition)).start();
}
}
可以看到condition最重要的两个方法是await()阻塞挂起,和signal()唤醒阻塞线程
我们来看一下condition调用的await()方法,该方法调用的是AQS的await()方法
首先是要创建节点,这里我们要先了解一下节点的状态waitStatus的以下几种状态,waitStatus初始值为0
* Status field, taking on only the values:
* SIGNAL: The successor of this node is (or will soon be)//只要前置节点释放锁,就会通知标识为signal状态的后续节点线程
* blocked (via park), so the current node must
* unpark its successor when it releases or
* cancels. To avoid races, acquire methods must
* first indicate they need a signal,
* then retry the atomic acquire, and then,
* on failure, block.
* CANCELLED: This node is cancelled due to timeout or interrupt.//获取锁超时或者被中断,不会再有机会获取锁
* Nodes never leave this state. In particular,
* a thread with cancelled node never again blocks.
* CONDITION: This node is currently on a condition queue.//处于等待队列
* It will not be used as a sync queue node
* until transferred, at which time the status
* will be set to 0. (Use of this value here has
* nothing to do with the other uses of the
* field, but simplifies mechanics.)
* PROPAGATE: A releaseShared should be propagated to other//共享锁模式下的节点状态,处于这个状态下的节点,会对线程的唤醒进行传播
* nodes. This is set (for head node only) in
* doReleaseShared to ensure propagation
* continues, even if other operations have
* since intervened.
* 0: None of the above
*
* The values are arranged numerically to simplify use.
* Non-negative values mean that a node doesn't need to
* signal. So, most code doesn't need to check for particular
* values, just for sign.
*
* The field is initialized to 0 for normal sync nodes, and
* CONDITION for condition nodes. It is modified using CAS
* (or when possible, unconditional volatile writes).
*/
volatile int waitStatus;
Node node = addConditionWaiter();//添加一个节点到等待队列
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {//如果等待队列里面的最后一个节点不是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;
}
接下来我们看下fullyRelease方法
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;
}
}
while (!isOnSyncQueue(node)) {//判断节点是否存在同步队列
LockSupport.park(this);//阻塞当前线程
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
下图来源于《Java并发编程的艺术》一书
下面来看下signal()方法
可以看到Condition的signal()方法也是调用的AQS的signal方法
public final void signal() {
if (!isHeldExclusively())//判断是否是当前线程调用的signal,如果不是就是其他线程通过中断调用
throw new IllegalMonitorStateException();
Node first = firstWaiter;//获取等待队列的第一个节点
if (first != null)
doSignal(first);
}
/**
* Removes and transfers nodes until hit non-cancelled one or
* null. Split out from signal in part to encourage compilers
* to inline the case of no waiters.
* @param first (non-null) the first node on condition queue
*/
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))//如果失败,表示节点被CANCELLED
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);//返回node的prev节点
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))//如果上一个节点的状态被取消了, 或者尝试设置上一个节点的状态为 SIGNAL 失败了(SIGNAL 表示: 他的 next 节点需要停止阻塞),
LockSupport.unpark(node.thread);
return true;//如果node的prev节点已经是signal状态,那么被阻塞的ThreadA的唤醒工作由AQS队列来完成
}
下图来源于《Java并发编程的艺术》一书
总结:线程调用condition.await()方法让线程进入等待队列,调用signal将线程从等队列移动到同步队列,在其他线程调用释放锁后有机会获得锁。