我有一个Stack类,就像这样
public class Stack {
LinkedList list = new LinkedList();
public synchronized void push(Object x) {
synchronized (list) {
list.addLast(x);
notify();
}
}
public synchronized Object pop () throws Exception {
synchronized (list) {
if (list.size() <= 0) {
wait();
}
return list.removeLast();
}
}
}
当多线程访问Stack类时,是否有一些多线程问题导致Stack崩溃?
答案 0 :(得分:1)
是否有一些多线程问题导致堆栈崩溃?
将来您应该始终提供异常详细信息,以便我们可以更好地帮助或至少定义“崩溃”的含义并提供日志输出。在这种情况下,我怀疑你得到NoSuchElementException
。
您的代码缺少一个小而重要的变化。
synchronized (list) {
// here's your problem, if should be while
if (list.size() <= 0) {
wait();
}
return list.removeLast();
}
这里的if
子句应该是while
循环。正如@Ivan指出的那样,如果您的架构受到虚假唤醒的影响,这一点很重要,但这也可以解决更可能的竞争条件。
如果您的堆栈中有多个线程消耗,则可能有2个线程等待pop()
,synchronized
块开头的线程A和{{1}的线程B }。当另一个线程执行wait()
和push()
时,等待的线程B将从等待队列移动到被阻塞的队列,但它将隐藏在线程A之后已被封锁。当执行推送的线程释放锁定时,线程A首先获得锁定并从列表中获取对象并解锁。然后线程B继续前进并调用notify()
,但堆栈中没有任何项目,它会抛出removeLast()
。
NoSuchElementException
通过使用// we use while here to protect against the race condition
while (list.size() <= 0) {
wait();
}
return list.removeLast();
,一旦线程A获得锁定,它就可以重新检查以确保另一个线程没有从被通知的堆栈中“窃取”该项目。有关此种族情况的详细信息,请参阅我的old page on this topic here。
结合其他评论:
while
上不需要 synchronized
方法和synchronized
块。我建议您只删除方法上的list
关键字,除非您没有向我们展示其他代码,只需在synchronized
上同步。然后,您应该list
。list.wait()
字段应为list
和private
,如果您final
上有synchronized
,则建议使用这些字段。