1.介绍
等待/通知不仅可以使用0bject的wait()/notify()、notifyAll()方法。还可用通过RenntrantLock下面
的Condition来实现。
2.使用
/**
* @Author AF
* @Description
* @Date 2019/12/28 18:19
*/
public class ConditionMain {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(new ConditionWait(lock,condition)).start();
new Thread(new ConditionSignal(lock,condition)).start();
}
}
public class ConditionWait implements Runnable {
//锁
private Lock lock;
//同步队列
private Condition condition;
public ConditionWait(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
try{
System.out.println("wait的锁地址"+lock);
lock.lock();
System.out.println("wait 开始阻塞");
//阻塞当前线程
condition.await();
System.out.println("wait阻塞结束");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("wait释放锁");
lock.unlock();
}
}
}
public class ConditionSignal implements Runnable {
private Lock lock;
private Condition condition;
public ConditionSignal(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
try {
System.out.println("signal的锁地址"+lock);
lock.lock();
System.out.println("signal开始唤醒");
condition.signal();
System.out.println("signal唤醒结束");
} catch (Exception e) {
} finally {
System.out.println("signal释放锁");
lock.unlock();
}
}
}
//===============
wait的锁地址java.util.concurrent.locks.ReentrantLock@24824c[Unlocked]
wait 开始阻塞
signal的锁地址java.util.concurrent.locks.ReentrantLock@24824c[Unlocked]
signal开始唤醒
signal唤醒结束
signal释放锁
wait阻塞结束
wait释放锁
3.Condition方法介绍
//线程阻塞
void await():
//有期限的阻塞
boolean await(long time, TimeUnit unit):
//有期限的阻塞
long awaitNanos(long nanosTimeout)
//线程阻塞
void awaitUninterruptibly()
//线程阻塞直到某个时间
boolean awaitUntil(Date deadline)
//======释放================
//释放条件队列中的firstWaiter
void signal()
//释放条件队列中的所以
void signalAll();
//condition的阻塞,会让当前线程释放锁,并且进去条件队列中
//awaitUninterruptibly和await()的区别是,使用前者阻塞后,调用thread.interrupt()不会报错,后者会报错
4.流程分析
newCondition()实际上是new ConditionObject(),而ConditionObject是AQS的内部类。从其源码中可以看出ConditionObject 是包含头结点和尾节点的。
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
//头节点
private transient Node firstWaiter;
//尾节点
private transient Node lastWaiter;
结构如下
当调用await()方法时,就会创建一个当前线程的node添加进队列中。使用的也是AQS的Node类。
相对于CLH对垒,Condition的条件队列的结构就简单很多。
AQS 等待队列与 Condition 队列是两个相互独立的队列
- #await() 就是在当前线程持有锁的基础上释放锁资源,并新建 Condition 节点加入到 Condition 的队列尾部,阻塞当前线程
- #signal() 就是将 Condition 的头节点移动到 AQS 等待节点尾部,让其等待再次获取锁。
以下是 AQS 队列和 Condition 队列的出入结点的示意图,可以通过这几张图看出线程结点在两个队列中的出入关系和条件。
I.初始化状态:AQS等待队列有 3 个Node,Condition 队列有 1 个Node(也有可能 1 个都没有)
II.节点1执行 Condition.await()
- 将 head 后移
- 释放节点 1 的锁并从 AQS 等待队列中移除
- 将节点 1 加入到 Condition 的等待队列中
- 更新 lastWaiter 为节点 1
III.节点 2 执行 Condition.signal() 操作
- 将 firstWaiter后移
- 将节点 4 移出 Condition 队列
- 将节点 4 加入到 AQS 的等待队列中去
- 更新 AQS 的等待队列的 tail
5.源码分析
await()
public final void await() throws InterruptedException {
//如果线程中断抛异常
if (Thread.interrupted())
throw new InterruptedException();
//创建当前线程的节点
Node node = addConditionWaiter();
//释放当前线程占用的锁。
int savedState = fullyRelease(node);
int interruptMode = 0;
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)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
private Node addConditionWaiter() {
//获取尾节点
Node t = lastWaiter;
//Node的节点状态如果不为CONDITION,则表示该节点不处于等待状态,需要清除节点
if (t != null && t.waitStatus != Node.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;
}
//释放锁
final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
//当使用await()之前,未使用lock.lock()加锁,则会抛出异常
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
//如果一个节点刚开始在条件队列上,现在在同步队列上获取锁则返回 true
final boolean isOnSyncQueue(Node node) {
// 状态为 Condition,获取前驱节点为 null ,返回 false
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
// 后继节点不为 null,肯定在 CLH 同步队列中
if (node.next != null)
return true;
return findNodeFromTail(node);
}
// 等待队列是一个单向链表,遍历链表将已经取消等待的节点清除出去
// 纯属链表操作,很好理解,看不懂多看几遍就可以了
private void unlinkCancelledWaiters() {
Node t = firstWaiter;
Node trail = null; // 用于中间不需要跳过时,记录上一个 Node 节点
while (t != null) {
Node next = t.nextWaiter;
// 如果节点的状态不是 Node.CONDITION 的话,这个节点就是被取消的
if (t.waitStatus != Node.CONDITION) {
t.nextWaiter = null;
if (trail == null)
firstWaiter = next;
else
trail.nextWaiter = next;
if (next == null)
lastWaiter = trail;
}
else
trail = t;
t = next;
}}
通知signal()
public final void signal() {
//检车当前线程是否为拥有锁的线程,如果不是,则报错
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
//唤醒firstWaiter
doSignal(first);
}
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
//移除头节点,将下一个节点作为头结点
Node next = first.nextWaiter;
first.nextWaiter = null;
//将原来的头结点移到CLH队列中
transferForSignal(first);
first = next;
} while (first != null);
}
//调节队列中节点移到同步队列中
final boolean transferForSignal(Node node) {
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}