【发布时间】:2021-01-08 12:08:38
【问题描述】:
我正在学习 Java 中的 synchronized 代码块和 .wait()/.notify() 方法,并且很难理解它们在生产者-消费者设置中的交互方式。下面类的相同实例被传递给两个线程;一个线程运行生产者方法,另一个线程运行消费者方法。
private Queue<Integer> queue = new LinkedList<>();
private Object lock = new Object();
public void producer() throws InterruptedException {
Random random = new Random();
while (true) {
synchronized(lock) {
while (queue.size() > 10) {
lock.wait();
}
queue.add(random.nextInt(100));
lock.notify();
}
}
}
public void consumer() throws InterruptedException {
while (true) {
synchronized(lock) {
if (queue.isEmpty()) {
lock.wait();
}
int val = queue.remove();
System.out.println(val + ": " + queue.size());
lock.notify();
}
}
}
}
这里,synchronized 在同一个对象上使得 只有 两个代码块之一同时运行。假设生产者线程赢得比赛,向队列中添加一个元素,并调用 notify。此时,消费者线程将在消费者函数中的synchronized(lock) 处等待(由于sycnhornized,它永远不会进入其代码块)。一旦生产者线程退出其同步代码块,消费者线程将进入其同步代码块。现在,队列是非空的,因为生产者只是在通知之前放入了一些东西。消费者线程将删除它,调用通知,退出它的块,此时生产者将获得锁,因为它现在一直在生产者函数中的synchronized(lock) 行等待。三个问题:
-
在我看来,我们在生产者和消费者之间交替,因此队列大小将在 0 和 1 之间波动。我错过了什么?
-
既然退出同步代码块释放了等待线程可以看到和获取的锁,为什么我们需要整个等待和通知机制?在我看来,我上面描述的
notify似乎没有做任何事情,因为一旦锁可用,另一个线程就会获取它并进入它的代码块。 -
lock.notify()是否也会唤醒在synchronized(lock)处等待的线程?
【问题讨论】:
标签: java multithreading concurrency synchronized producer-consumer