翻译:等待的最佳方法是在等待调用四周的 while 循环中检查正在等待的条件,如下面的例子所示。这种方法可以避免由随机唤醒引起的问题。
这个方案其实是操作系统原生的 api 就建议过 wait 搭配 while,Java 只是继续前人的意志,当然具体你要使用 if 还是使用 while 还是要看具体问题具体分析的。 因为 wait 可能会被 interrupt() 给唤醒,如果使用 if 作为判定,此时就可能会存在 wait 被提前唤醒的 情况。
notify
当我们使用 put 和 take 方法的时候记得抛出 InterruptedException 非常
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class Demo2 {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
queue.put(10);
queue.take();
}
}
复制代码
模仿实现
现在我们来模仿实现一个基于数组实现的阻塞循环队列。
class MyBlockingQueue {
private int[] elem;
private int size;
private int head;
private int tail;
Object locker = new Object();
public MyBlockingQueue(int capacity) {
elem = new int[capacity];
}
public void put(int x) throws InterruptedException {
synchronized (locker) {
while (size == elem.length) {
locker.wait();
}
elem[tail] = x;
tail++;
if (tail == elem.length) {
tail = 0;
}
size++;
locker.notify();
}
}
public int take() throws InterruptedException {
synchronized (locker) {
while (size == 0) {
locker.wait();
}
int x = elem[head];
head++;
if (head == elem.length) {
head = 0;
}
size--;
locker.notify();
return x;
}
}
}
复制代码
这里着重介绍 wait 和 notify 的作用,当我们进行 put 操作的时候,如果发现队列已满,我们必要进入线程等待状态,比及 take 拿走元素之后就进行 notify 操作唤醒 put ;同理,当进行 take 操作的时候,如果发现队列为空,要进入等待状态,比及 put 放入元素之后进行 notify 操作唤醒 take