在 Java 中使用 wait() 和 notify() 被阻止
Blocked using wait() and notify() in Java
我在 Java 中使用 wait()
和 notify()
编写生产者和消费者代码。
Thread-0 创建并在 produce()
上调用,Thread-1 创建并在 consume()
.
上调用
public class Processor {
private volatile List<Integer> list = new ArrayList<>();
private final int MAX_CAPACITY = 5;
Object lock = new Object();
public void produce() throws InterruptedException {
while (true) {
while (list.size() == MAX_CAPACITY) {
System.out.println("List is full! Producer is Waiting....");
synchronized (lock) {
lock.wait();
}
}
synchronized (lock) {
int random = new Random().nextInt(100);
list.add(random);
System.out.println("Added to list:" + random);
lock.notify();
}
}
}
public void consume() throws InterruptedException {
while (true) {
while (list.size() == 0) {
System.out.println("List is empty!! Consumer is Waiting...");
synchronized (lock) {
lock.wait();
}
}
synchronized (lock) {
int i = list.remove(0);
System.out.println("Removed from list:" + i);
lock.notify();
}
}
}
}
问题是在执行过程中,程序在 produce()
:
之后停止
List is empty!! Consumer is Waiting...
Added to list:22
Added to list:45
Added to list:72
Added to list:91
Added to list:51
List is full! Producer is Waiting....
我不明白这里有什么问题。我不知何故发现将 while
循环中的代码包装在 produce()
和 consume()
中的 synchronized
块中可以解决问题。
produce()
synchronized (lock) {
while (list.size() == MAX_CAPACITY) {
System.out.println("List is full! Producer is Waiting....");
lock.wait();
}
consume
synchronized (lock) {
while (list.size() == 0) {
System.out.println("List is empty!! Consumer is Waiting...");
lock.wait();
}
}
这里有什么问题?是线程饥饿还是死锁?
编辑: 调用 class:
public class App {
public static void main(String[] args) {
final Processor processor = new Processor();
Runnable r1 = new Runnable() {
@Override
public void run() {
try {
processor.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
try {
processor.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
}
当您执行 list.size()
时,它不是线程安全的,并且不能保证您会在另一个线程中看到该值发生更改。如果 JIT 检测到您没有在该线程中更改它,它甚至可以内联该值。
通过将 synchronized
块放在循环之外,您可以确保值的变化是可见的(因为它也在 while(true)
循环内。
使用 synchronized
外部循环创建 read barrier。因此 producer/consumer 将在您正在检查 list.size()
的循环中看到最新的 list
。这就是为什么在 synchronized
块内移动 while
循环后它起作用的原因。
对于您的情况,我还建议您在 producer/consumer 中使用单个同步块。
例如,在您的实现中,如果 list.size() == 0
变为 false
用于消费者,它将释放对 lock
对象的锁定,然后在下一条语句中尝试再次重新获取锁定以进行消费数据,这是不必要且低效的。它应该是这样的:
public void consume() throws InterruptedException {
while (true) {
synchronized (lock) {
while (list.size() == 0) {
System.out.println("List is empty!! Consumer is Waiting...");
lock.wait();
}
int i = list.remove(0);
System.out.println("Removed from list:" + i);
lock.notify();
}
}
}
我在 Java 中使用 wait()
和 notify()
编写生产者和消费者代码。
Thread-0 创建并在 produce()
上调用,Thread-1 创建并在 consume()
.
public class Processor {
private volatile List<Integer> list = new ArrayList<>();
private final int MAX_CAPACITY = 5;
Object lock = new Object();
public void produce() throws InterruptedException {
while (true) {
while (list.size() == MAX_CAPACITY) {
System.out.println("List is full! Producer is Waiting....");
synchronized (lock) {
lock.wait();
}
}
synchronized (lock) {
int random = new Random().nextInt(100);
list.add(random);
System.out.println("Added to list:" + random);
lock.notify();
}
}
}
public void consume() throws InterruptedException {
while (true) {
while (list.size() == 0) {
System.out.println("List is empty!! Consumer is Waiting...");
synchronized (lock) {
lock.wait();
}
}
synchronized (lock) {
int i = list.remove(0);
System.out.println("Removed from list:" + i);
lock.notify();
}
}
}
}
问题是在执行过程中,程序在 produce()
:
List is empty!! Consumer is Waiting...
Added to list:22
Added to list:45
Added to list:72
Added to list:91
Added to list:51
List is full! Producer is Waiting....
我不明白这里有什么问题。我不知何故发现将 while
循环中的代码包装在 produce()
和 consume()
中的 synchronized
块中可以解决问题。
produce()
synchronized (lock) {
while (list.size() == MAX_CAPACITY) {
System.out.println("List is full! Producer is Waiting....");
lock.wait();
}
consume
synchronized (lock) {
while (list.size() == 0) {
System.out.println("List is empty!! Consumer is Waiting...");
lock.wait();
}
}
这里有什么问题?是线程饥饿还是死锁?
编辑: 调用 class:
public class App {
public static void main(String[] args) {
final Processor processor = new Processor();
Runnable r1 = new Runnable() {
@Override
public void run() {
try {
processor.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
try {
processor.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
}
当您执行 list.size()
时,它不是线程安全的,并且不能保证您会在另一个线程中看到该值发生更改。如果 JIT 检测到您没有在该线程中更改它,它甚至可以内联该值。
通过将 synchronized
块放在循环之外,您可以确保值的变化是可见的(因为它也在 while(true)
循环内。
使用 synchronized
外部循环创建 read barrier。因此 producer/consumer 将在您正在检查 list.size()
的循环中看到最新的 list
。这就是为什么在 synchronized
块内移动 while
循环后它起作用的原因。
对于您的情况,我还建议您在 producer/consumer 中使用单个同步块。
例如,在您的实现中,如果 list.size() == 0
变为 false
用于消费者,它将释放对 lock
对象的锁定,然后在下一条语句中尝试再次重新获取锁定以进行消费数据,这是不必要且低效的。它应该是这样的:
public void consume() throws InterruptedException {
while (true) {
synchronized (lock) {
while (list.size() == 0) {
System.out.println("List is empty!! Consumer is Waiting...");
lock.wait();
}
int i = list.remove(0);
System.out.println("Removed from list:" + i);
lock.notify();
}
}
}