我是Java多线程的初学者。这里有一个关于生产者-消费者模型和
wait
、notifyAll
方法的演示代码,但是我发现结果很难理解。
public class ProducerConsumerExample {
public static void main(String[] args) {
SharedResource sharedResource = new SharedResource();
Thread producerThread = new Thread(new Producer(sharedResource));
Thread consumerThread = new Thread(new Consumer(sharedResource));
producerThread.start();
consumerThread.start();
}
}
class SharedResource {
private int content;
private boolean available = false;
public synchronized void put(int value) {
while (available) {
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
content = value;
available = true;
notifyAll();
}
public synchronized int get() {
while (!available) {
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
available = false;
notifyAll();
return content;
}
}
class Producer implements Runnable {
private final SharedResource resource;
Producer(SharedResource resource) {
this.resource = resource;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
resource.put(i);
System.out.println("Produced: " + i);
}
}
}
class Consumer implements Runnable {
private final SharedResource resource;
Consumer(SharedResource resource) {
this.resource = resource;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("Consumed: " + resource.get());
}
}
}
实际运行结果如下
Produced: 0
Produced: 1
Consumed: 0
Consumed: 1
Produced: 2
Produced: 3
Consumed: 2
Consumed: 3
Consumed: 4
Produced: 4
Produced: 5
Produced: 6
Consumed: 5
Consumed: 6
Consumed: 7
Produced: 7
Consumed: 8
Produced: 8
Produced: 9
Consumed: 9
这样的结果令人愤慨。我相信结果应该是生产者的
put
和消费者的get
连续执行。换句话说,结果应该是这样的。
Produced: 0
Consumed: 0
Produced: 1
Consumed: 1
...
我认为流程应该是这样的:
生产者执行
put(0)
,
此时
available
是false
。然后,available
设置为true
,内容的值也设置为0
。
接下来,
notifyAll()
被执行。
此时,消费者线程可能还没有进入等待状态,但这不会对结果产生太大影响。
接下来,应该执行
put(1)
。此时,由于available
的值为true
,因此进入while循环,然后执行wait()
方法。
此时,生产者线程进入等待状态并释放锁。消费者线程在获取锁并看到
available
是 true
后,不会进入 while 循环。
然后将
available
设置为 false
并调用 notifyAll()
将生产者线程置于阻塞状态,同时返回内容的值。
假设生产者线程此时获得了锁并继续执行
put(1)
(即使消费者线程获得了锁并执行get(1)
,也没有关系,因为它会进入while循环并调用 wait()
方法,让生产者线程继续执行put(1)
),
如此循环。
此外,还有更离谱的结果;这次,消费者线程的结果甚至被先打印出来,像这样:
Consumed: 0
Produced: 0
Produced: 1
Produced: 2
Consumed: 1
Consumed: 2
Consumed: 3
...
有人可以解释一下这些结果到底是怎么回事吗?为什么和我想象的不一样?
问题的答案不是
synchronized
方法内部发生的事情 - 而是这些方法之外发生的事情。
synchronized
方法之外的操作在线程之间不排序。
一个可能的有效序列是:
put(0)
,然后写出“Produced:0”,然后调用put(1)
并且必须等待消费者get()
,但从 get()
返回后立即被暂停 - 在它可以打印任何内容之前put(1)
,然后写出“Produced:1”,然后调用put(2)
并且必须再次等待消费者另一个可能的有效序列是:
put(0)
,之后它在打印任何内容之前被挂起get()
,然后写出“Consumed:0”,然后再次调用get()
并且必须等待生产者put(1)