问题是:有两个线程,一个是 List 的写入者,另一个是 List 的读取者。如果编写器中的循环有大量迭代,有时阅读器会卡住。在那种情况下,那个读者变成了阻塞(不是等待),这意味着它收到了通知,但作者没有释放监视器?
那么,为什么会这样?最好的办法是什么?(睡得好吗?)
import java.util.LinkedList;
import java.util.List;
public class Main {
private List<Object> m_calls = new LinkedList<Object>();
public void startAll(){
Thread reader = new Thread(new Runnable() {
@Override
public void run() {
while(true){
synchronized(m_calls){
while (m_calls.size() == 0) {
try {
System.out.println("wait");
m_calls.wait();
} catch (InterruptedException e) {
return;
}
}
m_calls.remove(0);
System.out.println("remove first");
}
}
}
});
Thread writer = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0; i < 15; i++){
// UN-comment to have more consistent behavior
/*try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
synchronized(m_calls){
m_calls.add(new Object());
m_calls.notifyAll();
System.out.println("sent");
}
}
}
});
reader.start();
writer.start();
}
public static void main(String[] args) {
new Main().startAll();
}
}
运行上面的代码会给出不同的结果:
---------------------------------- 第一次尝试
等待
发送
发送
发送
发送
发送
发送
发送
发送
发送
发送
发送
发送
发送
移除
先
移除 先
移除
先
移除 先
移除 先
移除 先
移除 先
移除 先
移除 先
移除 先
移除 先
移除 先
移除先
移除
先
移除
---------------------------------- 第二次尝试
等待
发送
发送
发送
发送
发送
发送
移除 先
移除 先
移除 先
移除 先
移除 先
移除 先
等待
发送
发送
移除 先
移除 先
等待
发送
发送
发送
发送
发送
发送
移除
先
移除 先
移除 先
移除 先
移除 先
移除 先
移除 先
等待
------------------------------ 未注释的 sleep() - 符合我们的预期
等待
发送
删除第一个
等待
发送
删除第一个
等待
发送
删除第一个
等待
发送
删除第一个
等待
发送
删除第一个
等待
发送
删除第一个
等待
发送
删除第一个
等待
发送
删除第一个
等待
发送
删除第一个
等待
发送
删除第一个等待
发送
删除第一个
等待
发送
删除第一个
等待
发送
先删除
等待
发送
先删除
等待
发送
删除第一个
等待
编辑1:阅读器线程(其中之一)似乎不再等待,而是被阻塞了,看起来它的监视器收到了通知(在notifyAll()之后)但是编写器线程没有在其循环中释放锁,这是令人困惑的...