Condition条件控制
作用:主要是通过ReentrantLock类里面的newCondition()方法获取lock上的一个条件,可以用于线程之间的通信
1)通过condition能够更加精细的控制多线程的休眠await与唤醒signal
2)在一个锁中,可以为多线程间建立不同的condition
Condition的使用阻塞队列
public class Consumer implements Runnable{
private Queue<String> msg;
private Lock lock ;
private Condition condition ;
private BlockingDeque blockingDeque;
public Consumer(Queue<String> msg ,Lock lock ,Condition condition ) {
this.msg = msg;
this.lock = lock;
this.condition = condition ;
}
@Override
public void run() {
while (true){
lock.lock();
while (msg.isEmpty()){
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消费者消费:"+msg.remove());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
condition.signal();
lock.unlock();
}
}
}
public class Producer implements Runnable{
private Queue<String> msg;
private Lock lock;
private Condition condition ;
private int maxSize;
public Producer(Queue<String> msg ,Lock lock ,Condition condition ,int maxSize) {
this.msg = msg;
this.lock = lock;
this.condition = condition ;
this.maxSize = maxSize;
}
@Override
public void run() {
int i = 0;
while (true){
i++;
lock.lock();
while (msg.size()==maxSize){
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("生成者生成:"+msg.add("生成者生成:"+i));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
condition.signal();
lock.unlock();
}
}
}
源码分析
调用await方法将线程放入到Condition队列中 -->释放锁–>判断当前节点是否在AQS同步队列中,如果在的话进行park阻塞通过node.waitStatus判断是否为condition node.pref前驱节点是否存在,最后一种为循环判断
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();、
//创建节点(单向),并把抢占到锁的节点移动到condition单向队列上
Node node = addConditionWaiter();
//完整释放锁(statu5->代表重入5次,完全释放相当于直接把5-》0)
//并且唤醒等待队列的线程
int savedState = fullyRelease(node);
int interruptMode = 0;
//判断这个节点是否在同步队列中,通过node.waitStatus判断是否为condition node.pref前驱节点是否存在,最后一种为循环判断
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
//去除状态为cancled状态的节点
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
//完整释放锁(statu5->代表重入5次,完全释放相当于直接把5-》0)
final int fullyRelease(Node node) {
boolean failed = true;
try {
//获取锁的statu值
int savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
public final boolean release(int arg) {
//
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//唤醒处于阻塞状态中的线程,相当于锁的释放
unparkSuccessor(h);
return true;
}
return false;
}
//创建Condition单向队列
private Node addConditionWaiter() {
Node t = lastWaiter;//最后一个等待
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
//去除状态为cancled状态的节点
unlinkCancelledWaiters();
t = lastWaiter;
}
//构建一个Node(CONDITION条件)队列,传递当前的线程
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
signal唤醒
(有两种方式抢占:1、原Tail为CANCELLED状态会跳过unlock,是一个性能优化的体现,早一点唤醒(提前抢占锁)执行剩下的代码2、condition的节点transfer)
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);
}
final boolean transferForSignal(Node node) {
//如果Node状态为CONDITION则修改为0 返回true,如果异常则返回false
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
//将Condition队列中的节点传输到AQS队列中,并且将执行AQS队列的逻辑
Node p = enq(node);
//p为AQS队列中的尾结点
int ws = p.waitStatus;
//当大于0时属于canceled状态(判断AQS队列中的尾结点tail是否为cancle(取消)状态) 判断ws 是否与 Node.SIGNAL状态相等
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}