这是作业者的生产者消费者模式的一种实现。下面的实现有什么问题。这会导致死锁。我不明白出了什么问题。
我有一个共享队列
我在同一锁上同步生产者和消费者
private static volatile Queue<Integer> BUFFER = new LinkedList<>();
private static int COUNT = 0;
private static final int SIZE = 1;
public static void main(String[] args) {
new Thread(() -> {
while (true) {
while (BUFFER.size() == SIZE) {
synchronized (BUFFER) {
try {
System.out.println("Producer waiting");
BUFFER.wait();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
synchronized (BUFFER) {
System.out.println("Producer added : " + COUNT);
BUFFER.offer(COUNT++);
System.out.println("Producer notify");
BUFFER.notify();
}
try {
Thread.sleep(500l);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
while (true) {
while (BUFFER.isEmpty()) {
synchronized (BUFFER) {
try {
System.out.println("Consumer waiting");
BUFFER.wait();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
synchronized (BUFFER) {
System.out.println("Consumer consumed : "+ BUFFER.poll());
BUFFER.notify();
}
try {
Thread.sleep(500l);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
}
这段代码会在一段时间后导致死锁。
Producer added : 0
Producer notify
Consumer consumed : 0
Consumer waiting
Producer added : 1
Producer notify
Consumer consumed : 1
Producer added : 2
Producer notify
Consumer waiting
Producer waiting
答案 0 :(得分:0)
您的代码容易受到丢失通知的影响。这是可能发生的情况:
BUFFER.notify()
。BUFFER.wait()
(因为缓冲区在检查时为空)。BUFFER.size() == 1
)。BUFFER.wait()
每个线程现在都在等待另一个线程做某事:死锁!
步骤2中的通知丢失。如果另一个线程尚未等待,BUFFER.notify()
根本不执行任何操作。
实际上只有一种正确的方法来使用wait()
和notify()
。您可以在这里阅读有关内容:https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
答案 1 :(得分:0)
为生产者消费者使用notify
是不正确的方法。
如果在调用notify()
时没有人真正在等待该通知,则通知会丢失。
您应该使用wait()
。而不是使用notify()
和semaphore
。
共享的semaphore
可以被任何线程释放和获取,并且是在这种类型的线程间通信中使用的正确工具。
在这种情况下,消费者将从队列中消费,release()
从semaphore
中消费。生产者将acquire()
信号量并将某些内容推入队列。
如果队列已满,则生产者将在尝试获取semaphore
时阻塞,直到消费者清空队列。
与您基本上在这里使用的条件变量不同,通过使用wait()
和notify()
,semaphore
保持状态。
有关Udemy的“ Java多线程,并发和性能优化”课程中的绑定队列和未绑定队列的示例均进行了深入讨论。
它说明了何时使用条件变量以及何时以及如何使用信号量。
我希望对您有帮助
答案 2 :(得分:0)
以下实现有什么问题。这会导致死锁。我不明白出了什么问题。
while (BUFFER.isEmpty()) {
synchronized (BUFFER) {
您的代码很接近,但是synchronized
关键字必须在此处包含while循环才能起作用,否则它将受到@SolomonSlow列举的竞争条件的影响。这不是并发集合,尽管您将其标记为volatile
,但不能保证isEmpty()
方法将返回true,可能会将元素添加到列表中,然后使用者输入{{ 1}}块。您需要确保测试和等待发生在同一锁中。
一旦移动了synchronized
关键字,就可以删除synchronized
,因为对volatile
的所有访问都在BUFFER
内部。
synchronized
通过将同步移到两个同时,您的代码似乎对我有用。
其他评论:
synchronized (BUFFER) {
while (BUFFER.size() == SIZE) {
...
synchronized (BUFFER) {
while (BUFFER.isEmpty()) {
和System.out.println()
调用包含在原始notify()
中。那里不需要2个街区。synchronized
和TODO。e.printStackTrace();
,而应使其尽可能地精细。 Exception
。当您抓到catch InterruptedException
时,应根据策略始终重新中断线程。
InterruptedException