我正在解决消费者/生产者问题,以熟悉 Java 中的并发问题。我的问题可能与 C/P 问题本身相邻。在下面的代码片段中,如果我使用版本 1,程序似乎运行良好,而使用版本 2,我似乎在一段可变的时间后陷入死锁。
我只会发布 Producer 类,因为不需要 Consumer 类。
- 缓冲区是生产者存放其输出的地方
- 我只是将 1 的整数添加到其中
- while(true)是让线程持续运行
我的问题是:我知道在Java API中调用wait()的结构是:synchronized -> while -> wait。但是在 while -> synchronized -> wait 的情况下会发生什么?如果在wait()期间,Consumer调用notifyAll()并且Producer再次唤醒,那么代码不会从调用wait()的地方继续,最终会到达while(buffer.remainingCapacity() == 0) ?
我已经进行了挖掘,看看以前是否有人问过这个问题,但找不到任何具体的内容。
版本 1
public class Producer extends Thread {
ArrayBlockingQueue<Integer> buffer = new ArrayBlockingQueue<>(10);
public Producer(ArrayBlockingQueue<Integer> buffer) {
this.buffer = buffer;
}
@Override
public void run() {
while (true) {
synchronized(buffer) {
while (buffer.remainingCapacity() == 0) {
try {
buffer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
try {
synchronized (buffer) {
buffer.add(1);
System.out.println("Producer active with remaining capacity: " + buffer.remainingCapacity());
buffer.notifyAll();
}
} catch (IllegalStateException ex) {
ex.printStackTrace();
}
}
}
}
版本 2
public class Producer extends Thread {
ArrayBlockingQueue<Integer> buffer = new ArrayBlockingQueue<>(10);
public Producer(ArrayBlockingQueue<Integer> buffer) {
this.buffer = buffer;
}
@Override
public void run() {
while (true) {
while (buffer.remainingCapacity() == 0) {
synchronized (buffer) {
try {
buffer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
try {
synchronized (buffer) {
buffer.add(1);
System.out.println("Producer active with remaining capacity: " + buffer.remainingCapacity());
buffer.notifyAll();
}
} catch (IllegalStateException ex) {
ex.printStackTrace();
}
}
}
}
只是为了解决这个问题,这是该问题的另一个版本代码,该代码似乎有效。我只是将其放在这里,以防对其他人有帮助,或者是否有人可以告诉我它是否错误以及原因。与上面的版本相比,这个版本的 wait 和 notificationAll 方法在代码中的位置发生了“翻转”。
while(true) {
while(buffer.remainingCapacity() == 0){
synchronized (buffer){
buffer.notifyAll();
}
}
try {
buffer.add(1);
System.out.println("Producer active with remaining capacity: " + buffer.remainingCapacity());
} catch (IllegalStateException ex) {
synchronized (buffer){
try {
buffer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
最佳答案
同步要考虑的事情是“什么操作需要是原子的?”在您的示例中,检查然后等待必须是原子的。否则,请考虑以下事件顺序:
- 线程 1:检查已执行但失败
- 线程2:更新缓冲区以更改检查结果
- 线程2:调用通知
- 线程1:WAITING调用
在这种情况下,Thread1 错过了通知并阻塞,直到另一个通知到来。将检查放在同步块(synchronized block)内可以消除这种可能性,因为在 Thread1 调用 wait 之前,Thread2 无法调用 notification。
关于Java wait - 在 while 循环内/外同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27218779/