生产者消费者没有给出期望的结果
Producer consumer not giving desired result
我正在从 java documentation. I implemented a famous problem Producer-consumer problem. But it is not giving result as expected. I've searched a lot about this problem at HERE, HERE, HERE, HERE, HERE 和其他一些堆栈交换和非堆栈交换站点学习线程同步,但无法解决我的问题。这是我的代码:
GetSetItem.java
public class GetSetItem {
private volatile boolean available = false;
private int item;
public synchronized void set(int item) {
while(available) {
try {
wait();
} catch (InterruptedException ie) {
System.err.println("Interrupted: " + ie.getMessage());
}
}
this.item = item;
available = true;
notifyAll();
}
public synchronized int get() {
while(!available) {
try {
wait();
} catch (InterruptedException ie) {
System.err.println("Interrupted: " + ie.getMessage());
}
}
available = false;
notifyAll();
return item;
}
}
Consumer.java
public class Consumer implements Runnable {
private int number; // Just for show #1,#2 etc. For future use
private GetSetItem consumer;
public Consumer(GetSetItem item, int seq) {
consumer = item;
number = seq;
}
@Override
public void run() {
int value = -1;
for(int i = 0; i < 10; i++) {
value = consumer.get();
System.out.println("Consumer #" + number + " get: " + value);
}
}
}
Producer.java
public class Producer implements Runnable {
private GetSetItem producer;
private int number = 0;
public Producer(GetSetItem item, int seq) {
producer = item;
number = seq;
}
@Override
public void run() {
for(int i = 0; i < 10; i++) {
producer.set(i);
System.out.println("Producer #" + number + " Put: " + i);
}
}
}
ProducerConsumerMain.java
public class ProducerConsumerMain {
public static void main(String[] args) {
GetSetItem item = new GetSetItem();
Producer p = new Producer(item, 1);
Consumer c = new Consumer(item, 1);
new Thread(p).start();
new Thread(c).start();
}
}
输出为:
Consumer #1 get: 0
Producer #1 Put: 0
Producer #1 Put: 1
Consumer #1 get: 1
Producer #1 Put: 2
Consumer #1 get: 2
Producer #1 Put: 3
Consumer #1 get: 3
Producer #1 Put: 4
Producer #1 Put: 5
Consumer #1 get: 4
Consumer #1 get: 5
Producer #1 Put: 6
Producer #1 Put: 7
Consumer #1 get: 6
Consumer #1 get: 7
Consumer #1 get: 8
Producer #1 Put: 8
Producer #1 Put: 9
Consumer #1 get: 9
但输出应该是生产者 -> 消费者格式。这意味着消费者只有在生产者可用并生产的情况下才能消费该物品。我也试过 private boolean available = false
而不是 private volatile boolean available = false;
但没有收到预期的输出。
所以请告诉我我做错了什么,我怎样才能成功解决这个问题。
您的代码看起来不错,问题很可能是 System.out
不是线程安全的。您还需要同步您的 println()
呼叫:
@Override
public void run() {
for (int i = 0; i < 10; i++) {
producer.set(i);
synchronized (System.out) {
System.out.println("Producer #" + number + " Put: " + i);
}
}
}
然后,输出将类似于:
Producer #1 Put: 0
Producer #1 Put: 1
Consumer #1 get: 0
Consumer #1 get: 1
Producer #1 Put: 2
Producer #1 Put: 3
Consumer #1 get: 2
Consumer #1 get: 3
Producer #1 Put: 4
Consumer #1 get: 4
Producer #1 Put: 5
Producer #1 Put: 6
Consumer #1 get: 5
Consumer #1 get: 6
Consumer #1 get: 7 <<<<
Producer #1 Put: 7 <<<<
Producer #1 Put: 8
Consumer #1 get: 8
Consumer #1 get: 9 <<<<
Producer #1 Put: 9 <<<<
您的线程仍有可能在 get/set
和 println
语句之间暂停,在这种情况下,您的消费者似乎正在消费尚未生产的东西,例如 where我在上面的输出中指出。不过,这只是一个输出问题,您的代码运行良好并且执行了它应该执行的操作。
我通过使用 get()
中的 System.out.println(...)
语句和 GetSetItem
class 的 set()
方法并从中删除 System.out.println(...)
解决了这个问题对应producer
和consumer
class。作为:
get() 方法 GetSetItem.java
public synchronized void set(int item, int number) {
while(available) {
try {
wait();
} catch (InterruptedException ie) {
System.err.println("Interrupted: " + ie.getMessage());
}
}
this.item = item;
/* Putting this line here gives expected output because this
* statement is synchronized due to method synchronization.
*/
System.out.println("Producer #" + number + " Produced: " + item);
available = true;
notifyAll();
}
set() 方法 of GetSetItem.java
public synchronized int get(int number) {
while (!available) {
try {
wait();
} catch (InterruptedException ie) {
System.err.println("Interrupted: " + ie.getMessage());
}
}
/*
* Putting this line here gives expected output because this statement
* is synchronized due to method synchronization.
*/
System.out.println("Consumer #" + number + " Consumed: " + item);
available = false;
notifyAll();
return item;
}
编辑: 输出:
Producer #1 Produced: 0
Consumer #1 Consumed: 0
Producer #1 Produced: 1
Consumer #1 Consumed: 1
Producer #1 Produced: 2
Consumer #1 Consumed: 2
Producer #1 Produced: 3
Consumer #1 Consumed: 3
Producer #1 Produced: 4
Consumer #1 Consumed: 4
Producer #1 Produced: 5
Consumer #1 Consumed: 5
Producer #1 Produced: 6
Consumer #1 Consumed: 6
Producer #1 Produced: 7
Consumer #1 Consumed: 7
Producer #1 Produced: 8
Consumer #1 Consumed: 8
Producer #1 Produced: 9
Consumer #1 Consumed: 9
我正在从 java documentation. I implemented a famous problem Producer-consumer problem. But it is not giving result as expected. I've searched a lot about this problem at HERE, HERE, HERE, HERE, HERE 和其他一些堆栈交换和非堆栈交换站点学习线程同步,但无法解决我的问题。这是我的代码:
GetSetItem.java
public class GetSetItem {
private volatile boolean available = false;
private int item;
public synchronized void set(int item) {
while(available) {
try {
wait();
} catch (InterruptedException ie) {
System.err.println("Interrupted: " + ie.getMessage());
}
}
this.item = item;
available = true;
notifyAll();
}
public synchronized int get() {
while(!available) {
try {
wait();
} catch (InterruptedException ie) {
System.err.println("Interrupted: " + ie.getMessage());
}
}
available = false;
notifyAll();
return item;
}
}
Consumer.java
public class Consumer implements Runnable {
private int number; // Just for show #1,#2 etc. For future use
private GetSetItem consumer;
public Consumer(GetSetItem item, int seq) {
consumer = item;
number = seq;
}
@Override
public void run() {
int value = -1;
for(int i = 0; i < 10; i++) {
value = consumer.get();
System.out.println("Consumer #" + number + " get: " + value);
}
}
}
Producer.java
public class Producer implements Runnable {
private GetSetItem producer;
private int number = 0;
public Producer(GetSetItem item, int seq) {
producer = item;
number = seq;
}
@Override
public void run() {
for(int i = 0; i < 10; i++) {
producer.set(i);
System.out.println("Producer #" + number + " Put: " + i);
}
}
}
ProducerConsumerMain.java
public class ProducerConsumerMain {
public static void main(String[] args) {
GetSetItem item = new GetSetItem();
Producer p = new Producer(item, 1);
Consumer c = new Consumer(item, 1);
new Thread(p).start();
new Thread(c).start();
}
}
输出为:
Consumer #1 get: 0
Producer #1 Put: 0
Producer #1 Put: 1
Consumer #1 get: 1
Producer #1 Put: 2
Consumer #1 get: 2
Producer #1 Put: 3
Consumer #1 get: 3
Producer #1 Put: 4
Producer #1 Put: 5
Consumer #1 get: 4
Consumer #1 get: 5
Producer #1 Put: 6
Producer #1 Put: 7
Consumer #1 get: 6
Consumer #1 get: 7
Consumer #1 get: 8
Producer #1 Put: 8
Producer #1 Put: 9
Consumer #1 get: 9
但输出应该是生产者 -> 消费者格式。这意味着消费者只有在生产者可用并生产的情况下才能消费该物品。我也试过 private boolean available = false
而不是 private volatile boolean available = false;
但没有收到预期的输出。
所以请告诉我我做错了什么,我怎样才能成功解决这个问题。
您的代码看起来不错,问题很可能是 System.out
不是线程安全的。您还需要同步您的 println()
呼叫:
@Override
public void run() {
for (int i = 0; i < 10; i++) {
producer.set(i);
synchronized (System.out) {
System.out.println("Producer #" + number + " Put: " + i);
}
}
}
然后,输出将类似于:
Producer #1 Put: 0
Producer #1 Put: 1
Consumer #1 get: 0
Consumer #1 get: 1
Producer #1 Put: 2
Producer #1 Put: 3
Consumer #1 get: 2
Consumer #1 get: 3
Producer #1 Put: 4
Consumer #1 get: 4
Producer #1 Put: 5
Producer #1 Put: 6
Consumer #1 get: 5
Consumer #1 get: 6
Consumer #1 get: 7 <<<<
Producer #1 Put: 7 <<<<
Producer #1 Put: 8
Consumer #1 get: 8
Consumer #1 get: 9 <<<<
Producer #1 Put: 9 <<<<
您的线程仍有可能在 get/set
和 println
语句之间暂停,在这种情况下,您的消费者似乎正在消费尚未生产的东西,例如 where我在上面的输出中指出。不过,这只是一个输出问题,您的代码运行良好并且执行了它应该执行的操作。
我通过使用 get()
中的 System.out.println(...)
语句和 GetSetItem
class 的 set()
方法并从中删除 System.out.println(...)
解决了这个问题对应producer
和consumer
class。作为:
get() 方法 GetSetItem.java
public synchronized void set(int item, int number) {
while(available) {
try {
wait();
} catch (InterruptedException ie) {
System.err.println("Interrupted: " + ie.getMessage());
}
}
this.item = item;
/* Putting this line here gives expected output because this
* statement is synchronized due to method synchronization.
*/
System.out.println("Producer #" + number + " Produced: " + item);
available = true;
notifyAll();
}
set() 方法 of GetSetItem.java
public synchronized int get(int number) {
while (!available) {
try {
wait();
} catch (InterruptedException ie) {
System.err.println("Interrupted: " + ie.getMessage());
}
}
/*
* Putting this line here gives expected output because this statement
* is synchronized due to method synchronization.
*/
System.out.println("Consumer #" + number + " Consumed: " + item);
available = false;
notifyAll();
return item;
}
编辑: 输出:
Producer #1 Produced: 0
Consumer #1 Consumed: 0
Producer #1 Produced: 1
Consumer #1 Consumed: 1
Producer #1 Produced: 2
Consumer #1 Consumed: 2
Producer #1 Produced: 3
Consumer #1 Consumed: 3
Producer #1 Produced: 4
Consumer #1 Consumed: 4
Producer #1 Produced: 5
Consumer #1 Consumed: 5
Producer #1 Produced: 6
Consumer #1 Consumed: 6
Producer #1 Produced: 7
Consumer #1 Consumed: 7
Producer #1 Produced: 8
Consumer #1 Consumed: 8
Producer #1 Produced: 9
Consumer #1 Consumed: 9