多线程问题,大家来找错。。。

今天看了一个有BUG的多线程代码,但是怎么看也没看出问题来,高手来解释解释吧.

代码:

import java.util.*;
public class PrintQueue {
private LinkedList<String> queue = new LinkedList<String>();
private final Object lock = new Object();
public void enqueue(String str) {
synchronized (lock) {
queue.addLast(str);
lock.notifyAll();
}
}
public void work() {
String current;
synchronized(lock) {
if (queue.isEmpty()) {
try {
lock.wait();
} catch (InterruptedException e) {
assert (false);
}
}
current = queue.removeFirst();
}
System.out.println(current);
}
public static void main(String[] args) {
final PrintQueue pq = new PrintQueue();
Thread producer1 = new Thread() {
public void run() {
pq.enqueue("anemone");
pq.enqueue("tulip");
pq.enqueue("cyclamen");
}
};
Thread producer2 = new Thread() {
public void run() {
pq.enqueue("iris");
pq.enqueue("narcissus");
pq.enqueue("daffodil");
}
};
Thread consumer1 = new Thread() {
public void run() {
pq.work();
pq.work();
pq.work();
pq.work();
}
};
Thread consumer2 = new Thread() {
public void run() {
pq.work();
pq.work();
}
};
producer1.start();
consumer1.start();
consumer2.start();
producer2.start();
}
}


问题补充
AllenZhang 写道
两个work工作,分别都进入了wait状态


这个是有同步锁的怎么可能两个都进呢?
synchronized(lock)

。。。。。。。。
wait会释放锁

当queue为空,两个work工作,分别都进入了wait状态。
当queue中有放入一个元素的时候,因为notifyAll,所有work都被唤醒。
唤醒后,会分别执行queue.removeFirst()一次,导致NoSuchElementException

这个问题比较典型啦。因为你使用的是notifyAll,所有wait阻塞的线程都会被唤醒,所以会出现两个线程都去removeFirst,但队列里只有一个元素的情况。
解决的办法就是wait应该放在循环体内,当线程被唤醒后,要再次检查条件是否成立才继续执行。把work方法里的
[code="java"]if (queue.isEmpty()) { [/code]
改成
[code="java"]while (queue.isEmpty()) { [/code]
就可以了。