概述
在某些情况下,线程成功获取到锁后发现仍需等待某些条件才能继续往下执行,这时该线程可以把锁释放并进入条件队列,等待持有该锁的其它线程使等待的条件满足,并再次唤醒在条件队列中的线程,被唤醒的线程会再次参与锁的竞争
监视器锁的条件队列
一个监视器锁只有一个条件队列
核心方法
object.wait()
当前线程把锁释放并进入条件队列object.notify()
唤醒条件队列中的任意一条线程object.notifyAll()
唤醒条件队列中的所有线程
例子
public class BoundedBuffer<V> {
private final V[] buf;
private int tail;
private int head;
private int count;
public BoundedBuffer(int capacity) {
this.buf = (V[]) new Object[capacity];
}
private void doPut(V v) {
buf[tail] = v;
if (++tail == buf.length) {
tail = 0;
}
++count;
}
private V doTake() {
V v = buf[head];
buf[head] = null;
if (++head == buf.length) {
head = 0;
}
--count;
return v;
}
private boolean isFull() {
return count == buf.length;
}
private boolean isEmpty() {
return count == 0;
}
public synchronized void put(V v) throws InterruptedException {
while (isFull()) {
// 向缓冲区放入元素时,缓冲区已满,需等待
wait();
}
doPut(v);
// 放入元素后,唤醒等待队列中的全部线程,目的在唤醒等待缓冲区非空的全部线程
notifyAll();
}
public synchronized V take() throws InterruptedExecption {
while (isEmpty()) {
// 从缓冲区取出元素时,缓冲区已空,需等待
wait();
}
V v = doTake();
// 取出元素后,唤醒等待队列中的全部线程,目的在唤醒等待缓冲区非满的全部线程
notifyAll();
return v;
}
}
显式锁的条件队列
一个显式锁可以有多个条件队列
核心方法
condition.await()
当前线程把锁释放并进入条件队列condition.signal()
唤醒条件队列中的任意一条线程condition.signalAll()
唤醒条件队列中的所有线程
例子
public class BoundedBuffer<V> {
private final V[] buf;
private int tail;
private int head;
private int count;
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
public BoundedBuffer(int capacity) {
this.buf = (V[]) new Object[capacity];
}
private void doPut(V v) {
buf[tail] = v;
if (++tail == buf.length) {
tail = 0;
}
++count;
}
private V doTake() {
V v = buf[head];
buf[head] = null;
if (++head == buf.length) {
head = 0;
}
--count;
return v;
}
private boolean isFull() {
return count == buf.length;
}
private boolean isEmpty() {
return count == 0;
}
public void put(V v) throws InterruptedException {
lock.lock();
try {
while (isFull()) {
// 向缓冲区放入元素时,缓冲区已满,需等待
notFull.await();
}
doPut(v);
// 放入元素后,唤醒等待缓冲区非空的任意一条线程
notEmpty.signal();
} finally {
lock.unlock();
}
}
public V take() throws InterruptedExecption {
lock.lock();
try {
while (isEmpty()) {
// 从缓冲区取出元素时,缓冲区已空,需等待
notEmpty.await();
}
V v = doTake();
// 取出元素后,唤醒等待缓冲区非满的任意一条线程
notFull.signal();
return v;
} finally {
lock.unlock();
}
}
}
唤醒方法的选择
只有同时满足以下两个条件时,才能用notify
或singal
- 条件队列中所有线程等待的条件相同,且从
wait
方法返回后执行的操作相同 - 负责唤醒的线程在唤醒前所提供的条件,只够一条线程使用
参考文档
- Brain Goetz等,Java并发编程实战(Java Concurrency in Practice),机械工业出版社,2012:243-244,247
PREVIOUSRedis基本命令
NEXTJava数组类型以及数组强制转型