同时从列表中推入和弹出元素时发生死锁
下面代码的目的是实现一个像 Stack 一样的 LIFO 容器,同时,当检索一个元素时,它会检查列表中是否有任何现有元素,或者它会保留在检索元素的线程上,直到有新的元素为止元素被插入。
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();
}
}
但是
只要在 List 中没有元素的情况下调用 pop() 就可能会发生死锁。如何修改这个类来达到最初的目的,同时避免潜在的死锁。谢谢
The purpose of the following code is to implement a LIFO container just like a Stack, while, when retrieve an element, it will check if there is any existing element in the list, or it hold on thread that retrieving element until there is a new element get inserted.
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();
}
}
}
but deadlock may occur whenever call pop() where there is no element in the List. How to amend this Class to achieve the initial purpose while avoiding pontential deadlock. Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
关于这门课,我要改变的事情太多了,很难知道从哪里开始。最简单的解决方案是删除
synchronized(list)
块,因为这些块是多余的,只会导致问题,如您所见。另一种解决方案是使用 Java 内置的 Stack 类。您的类可能会与构建的类混淆,因为它具有相同的名称。
或者我会使用 LinkedBlockingDeque ,它作为我的集合是线程安全的,并且具有 pop() 和 push() 方法。
编辑:如果你想知道为什么会出现死锁,那是因为你持有两个锁(一个在堆栈上,另一个在 LinkedList 上),但你只用 wait() 释放了一个锁(一个)在堆栈上)意味着没有其他线程可以获得列表上的锁。无法同时对两个对象进行 wait()。
您的代码与此大致相同(可能更清晰)
There are so many things I would change about this class its hard to know where to start. This simplest solution is to remove the
synchronized(list)
blocks as these are redundant and just cause problems as you can see.Another solution would be to use the Stack class which is builtin to Java. Your class could be confused for the built one as it has the same name.
Or I would use a LinkedBlockingDeque which is thread safe as my collection and has pop() and push() methods.
EDIT: If you want to know why you are getting a dead lock, it is because you are holding two locks (one on the Stack, the other on the LinkedList) but you are releasing only one lock with your wait() (the one on the Stack) meaning no other thread can get the lock on the list. There is no way to wait() on two objects at once.
Your code is much the same as this (which might be clearer)
当您在空堆栈上调用 pop,然后从其他线程推送时,会发生这种情况。
pop
中的wait()
位于synchronized
块中,因此从另一个线程调用push
将等待pop
方法返回,但是pop
被阻塞等待push
并且你陷入了死锁。This what happens when you call pop on an empty Stack and then push from other thread.
The
wait()
inpop
is insynchronized
block so the call topush
from another thread will wait for thepop
method to return, butpop
is blocked waiting for apush
and you got your deadlock.问题是您正在两个不同的对象上同步:在堆栈实例上(通过两个方法上的
synchronized
关键字)和在列表上(通过synchronized (list)
语句)因此,当
pop
方法等待时,它会在堆栈实例上调用 wait,从而释放它在堆栈实例上持有的监视器,从而允许另一个线程进入push
方法。但它不会释放它在列表中持有的监视器,因此另一个线程被阻塞,等待列表上的监视器被释放。The problem is that you're synchronizing on two different objects : on the stack instance (through the
synchronized
keyword on both methods) and on the list (through thesynchronized (list)
statements)So, when the
pop
method waits, it calls wait on the stack instance and thus releases the monitor it holds on the stack instance, allowing another thread to enter thepush
method. But it doesn't release the monitor it holds on the list, so the other thread is blocked waiting for the monitor on the list to be released.你的问题是问题的组合。
首先,您的代码为每个方法获取两个锁 - 一个在堆栈上,一个在列表上。当调用“wait”时,堆栈上的那个被释放,但列表上的那个没有被释放。这将阻止执行任何其他方法。这实际上是一个僵局。 (感谢 Peter Lawrey)
其次,代码:
将导致尝试弹出操作等待推送,然后再继续。这看起来像是一个僵局,但实际上只是等待。然而调用
notify();
只会唤醒一个线程。如果有多个线程正在等待弹出,则除一个之外的所有线程都将继续等待。这看起来更像是一个僵局(尽管从技术上来说并非如此)。
您应该对此代码进行修复:
如果堆栈上没有项目,则抛出异常比堆栈抛出异常更正常。等待推动。
Your problem is a combination of problems.
Firstly your code gets two locks for every method - one on the Stack and one on the list. When 'wait' is called the one on the Stack is released, but the one on list is not. This will prevent any other methods being executed. This is effectively a deadlock. (Thanks Peter Lawrey)
Secondly the code:
will cause an attempted pop to wait for a push before continuing. That may seem like a deadlock, but really it's just waiting. However the call
notify();
will only wake up one thread. If more than one thread is waiting to pop, all but one will continue waiting. This seems even more like a deadlock (even though it isn't, technically).
Fixes you should do to this code:
It's also much more normal to have a stack throw an exception if there are no items on it rather than wait for a push.