消费者生产者问题——同步总是必要的吗?
Consumer Producer Problem - Is synchronization always necessary?
我的问题纯粹是概念性的。并且只是为了更深入地了解线程之间的通信。
在生产者消费者问题中,
- 有1个生产者线程和1个消费者线程。
- 生产者线程调用produce方法,Consumer线程调用consume方法。
The example code is taken from here
package ProducerConsumer;
import java.util.LinkedList;
import java.util.Queue;
public class ClassicProducerConsumerExample {
public static void main(String[] args) throws InterruptedException {
Buffer buffer = new Buffer(2);
Thread producerThread = new Thread(new Runnable() {
@Override
public void run() {
try {
buffer.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread consumerThread = new Thread(new Runnable() {
@Override
public void run() {
try {
buffer.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
producerThread.start();
consumerThread.start();
producerThread.join();
consumerThread.join();
}
static class Buffer {
private Queue<Integer> list;
private int size;
public Buffer(int size) {
this.list = new LinkedList<>();
this.size = size;
}
public void produce() throws InterruptedException {
int value = 0;
while (true) {
synchronized (this) {
while (list.size() >= size) {
// wait for the consumer
wait();
}
list.add(value);
System.out.println("Produced " + value);
value++;
// notify the consumer
notify();
Thread.sleep(1000);
}
}
}
public void consume() throws InterruptedException {
while (true) {
synchronized (this) {
while (list.size() == 0) {
// wait for the producer
wait();
}
int value = list.poll();
System.out.println("Consume " + value);
// notify the producer
notify();
Thread.sleep(1000);
}
}
}
}
}
我读到等待和通知应该在同步块内以避免竞争条件。
我不明白为什么当两个线程调用不同的方法时我应该将 wait() 和 notify() 包含在同步块中。消费者线程不会调用 produce(),所以如果我不使用 synchronized 关键字将 wait 调用包含在 produce 方法中,那么它的行为应该仍然相同,因为 produce() 仅由生产者线程调用。是吗?
两个线程 adding/removing 来自的缓冲区是一个链表。 Java的链表不是线程安全的。
但是,您可以使用 concurrent 版本将这种锁定抽象到数据结构本身。
另一个后果: 虽然在这个例子中只有生产者和一个消费者,但情况可能并非总是如此。拥有多个生产者 and/or 消费者可能会有一个需要同步的用例,即使对于线程安全的数据结构也是如此。
我的问题纯粹是概念性的。并且只是为了更深入地了解线程之间的通信。
在生产者消费者问题中,
- 有1个生产者线程和1个消费者线程。
- 生产者线程调用produce方法,Consumer线程调用consume方法。
The example code is taken from here
package ProducerConsumer;
import java.util.LinkedList;
import java.util.Queue;
public class ClassicProducerConsumerExample {
public static void main(String[] args) throws InterruptedException {
Buffer buffer = new Buffer(2);
Thread producerThread = new Thread(new Runnable() {
@Override
public void run() {
try {
buffer.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread consumerThread = new Thread(new Runnable() {
@Override
public void run() {
try {
buffer.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
producerThread.start();
consumerThread.start();
producerThread.join();
consumerThread.join();
}
static class Buffer {
private Queue<Integer> list;
private int size;
public Buffer(int size) {
this.list = new LinkedList<>();
this.size = size;
}
public void produce() throws InterruptedException {
int value = 0;
while (true) {
synchronized (this) {
while (list.size() >= size) {
// wait for the consumer
wait();
}
list.add(value);
System.out.println("Produced " + value);
value++;
// notify the consumer
notify();
Thread.sleep(1000);
}
}
}
public void consume() throws InterruptedException {
while (true) {
synchronized (this) {
while (list.size() == 0) {
// wait for the producer
wait();
}
int value = list.poll();
System.out.println("Consume " + value);
// notify the producer
notify();
Thread.sleep(1000);
}
}
}
}
}
我读到等待和通知应该在同步块内以避免竞争条件。
我不明白为什么当两个线程调用不同的方法时我应该将 wait() 和 notify() 包含在同步块中。消费者线程不会调用 produce(),所以如果我不使用 synchronized 关键字将 wait 调用包含在 produce 方法中,那么它的行为应该仍然相同,因为 produce() 仅由生产者线程调用。是吗?
两个线程 adding/removing 来自的缓冲区是一个链表。 Java的链表不是线程安全的。
但是,您可以使用 concurrent 版本将这种锁定抽象到数据结构本身。
另一个后果: 虽然在这个例子中只有生产者和一个消费者,但情况可能并非总是如此。拥有多个生产者 and/or 消费者可能会有一个需要同步的用例,即使对于线程安全的数据结构也是如此。