Condition提供一种方式让当前的线程挂起操作,直到被另外一个线程通知,因为这些线程的共享状态可能被多个线程访问,因此必须要加锁来保护。Condition在使用的时候必须要和锁在一起使用。
Condition增强实现了的Object.wait()方法,可以对同一个对象进行多次等待。
直接看下AQS中Condition对象的实现吧。
源码
1 首先拿一段来实验的代码,了解Condition怎么用
public class LockDemo {
static ReentrantLock lock = new ReentrantLock();
static Condition condition = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 8; i++) {
new Thread(() -> {
try {
lock.lock();
System.out.println(Thread.currentThread() + "locked");
condition.await();
System.out.println(Thread.currentThread() + "unlocked");
} catch (InterruptedException e) {
//e.printStackTrace();
} finally {
lock.unlock();
}
}).start();
}
Thread.sleep(2000);
lock.lock();
condition.signalAll();
lock.unlock();
new CountDownLatch(1).await();
}
}
2 看下Condition对象总体提供了那些方法
- await 进入等待状态,直到被signal唤醒,或者被中断。在调用的生活会释放与之相关的锁
- awaitUninterruptibly 只能被signal唤醒
- awaitNanos 等待指定的时间,其余同await
- await(long time, TimeUnit unit) 同上
- awaitUntil 同上
- signal 唤醒一个等待的线程
- signalAll 唤醒所有的等待线程
await方法
1 看下await方法做了那些事情
- 添加一个Waiter节点,Waiter节点仅仅作用在Condition对象中,与Lock的Node链表不是同一个
- 释放当前的锁,返回其状态
- 判断当前的节点是否在同步队列中
- 对中断的支持,interruptMode
- 尝试获取锁
- 对中断标记位进行处理
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) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
await与awaitUninterruptibly对比
如果觉得await方法稍微有点不明白,那么看下awaitUninterruptibly,再看await方法。
- 添加Node.CONDITION节点
- 释放锁,返回同步器的状态
- 如果不在同步队列,进行阻塞,响应中断
-
Queue中的Node状态,在signal的时候会被修改
image-20200218085206524.png
-
signal方法
- 判断当前的线程是否持有排它锁
- 通知第一个节点
- 内部是Lock.unpark
- 然后将first指向第一个等待的节点
doSignalAll则是对所有的节点机械能通知
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);
}
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null);
}
最后
相对比较乱,后面还需要再来一次。
整体我觉得代码设计上值得学,如果拿出来问问题,稍微有点不太适合,有几个关键点估计就可以了。
- CLH,双向链表的操作
- Node的状态含义
- 说下对AQS的理解
- JUC中常见的工具类