循环同步死锁
我在 Java 中有以下类,
public class Counter {
private int value;
public Counter(int value) {
this.value = value;
}
public void setValue(int value) {
this.value = value;
}
public void decrement() {
this.value--;
}
public int getValue() {
return this.value;
}
}
public class Cell extends Thread {
private Object sync;
private Counter counter;
public Cell(Object sync, Counter counter) {
this.sync = sync;
this.counter = counter;
}
public void run() {
for (int r=0; r<Simulation.ROUND_NUM; r++) {
// do something
synchronized(counter) {
counter.decrement();
counter.notifyAll();
}
synchronized(sync) {
try {
sync.wait();
}
catch (Exception ex) {}
}
}
}
}
public class Simulation extends Thread {
public static final int THREAD_NUM = 5;
public static final int ROUND_NUM = 5;
public Object sync = new Object();
private Counter counter = new Counter(THREAD_NUM);
public void run() {
for (int i=0; i<THREAD_NUM; i++) {
Cell c = new Cell(sync,counter);
c.start();
}
for (int i=0; i<ROUND_NUM; i++) {
synchronized(counter) {
while(counter.getValue() != 0) {
try {
counter.wait();
}
catch (Exception ex) {}
}
counter.setValue(THREAD_NUM);
}
synchronized(sync) {
sync.notifyAll();
}
}
}
}
目的是防止在每个 Cell 线程中执行下一次循环迭代,直到每个 Cell 线程在每次迭代中完成。我的解决方案有时会导致僵局。我不明白为什么。请帮忙
I've got the following classes in Java
public class Counter {
private int value;
public Counter(int value) {
this.value = value;
}
public void setValue(int value) {
this.value = value;
}
public void decrement() {
this.value--;
}
public int getValue() {
return this.value;
}
}
public class Cell extends Thread {
private Object sync;
private Counter counter;
public Cell(Object sync, Counter counter) {
this.sync = sync;
this.counter = counter;
}
public void run() {
for (int r=0; r<Simulation.ROUND_NUM; r++) {
// do something
synchronized(counter) {
counter.decrement();
counter.notifyAll();
}
synchronized(sync) {
try {
sync.wait();
}
catch (Exception ex) {}
}
}
}
}
public class Simulation extends Thread {
public static final int THREAD_NUM = 5;
public static final int ROUND_NUM = 5;
public Object sync = new Object();
private Counter counter = new Counter(THREAD_NUM);
public void run() {
for (int i=0; i<THREAD_NUM; i++) {
Cell c = new Cell(sync,counter);
c.start();
}
for (int i=0; i<ROUND_NUM; i++) {
synchronized(counter) {
while(counter.getValue() != 0) {
try {
counter.wait();
}
catch (Exception ex) {}
}
counter.setValue(THREAD_NUM);
}
synchronized(sync) {
sync.notifyAll();
}
}
}
}
The aim is to prevent from executing the next iteration of loop in each Cell Thread, until every Cell Thread will be done on each iteration. My solution sometimes leads to deadlock. I can't understand why. Please help
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
首先,您可以使用 AtomicInteger 类而不是您创建的 Counter 类。 AtomicInteger 类是线程安全的,因此您可以使用原子操作,例如 decrementAndGet 和 incrementAndGet。
要实现等待每个单元线程完成的功能,您可以使用 CountDownLatch 就像之前的评论中提到的,甚至是并发对象,例如 CyclicBarriers 停止执行,直到所有 Cell 线程加入屏障。通过其中一些并发对象,应该更容易控制多个线程。使用普通同步也确实有效,只是通常需要您进行更多编码和思考以确保一切正常运行。
First of all you could make use of the AtomicInteger class instead of the Counter class you made. The AtomicInteger class is thread-safe so that you can use atomic action such as decrementAndGet and incrementAndGet.
To achieve the functionality of waiting till each of the Cell threads is done you can use a CountDownLatch like mentioned in a previous comment, or even concurrent objects like CyclicBarriers to halt execution till all Cell threads join on the barrier. Through some of these concurrent objects it should be easier to control multiple threads. Using plain synchronization does work as well, you just are typically required to do more coding and thinking to ensure everything works well.
在您的代码中,似乎无法保证当执行
sync.notifyAll()
时,所有 Cell 线程都会进入sync.wait()
。这是指最后一个 Cell 线程(示例中的第五个)需要获取sync
锁才能等待。但模拟线程也在尝试同样的事情,但没有确保每个人都在等待。这种竞争条件使得模拟有时会在最后一个单元能够执行相同操作并等待之前抢到锁。由于最后一个 Cell 没有等待,因此它不会收到通知,因此整个事情陷入困境。
您可以通过添加 System.out.println() 作为每个同步 (sync) 块中的第一行并相应地写入“等待同步”和“通知同步”来测试这一点。当您通知它时,您将看到只有 4 个线程正在等待同步。
为了确保模拟器通知时每个人都在等待,请在
Cell#run()
中嵌套两个同步块:In your code, there seems to be no guarantee that when
sync.notifyAll()
gets executed, all the Cell threads got tosync.wait()
. This refers to the last Cell thread (the fifth in your example) that needs to grab the lock forsync
in order to wait on it. But the Simulation thread is also trying the same thing without making sure that everyone is waiting. That race condition makes Simulation sometimes grab the lock before the last Cell is able to do the same and wait.Since that last Cell is not waiting, it doesn't get notified so the whole thing gets stuck.
You can test this by adding a System.out.println() as the first line in each
synchronized (sync)
block and writing "waiting for sync" and "notifying sync" accordingly. You'll see that only 4 threads are waiting for sync when you notify it.To make sure everyone is waiting when the Simulator notifies, have the two synchronized blocks in
Cell#run()
nested:您的代码可能会死锁,因为您无法保证在发生notifyAll 时Cell 线程实际上位于wait() 块中。以下是可能导致此问题的一系列事件:
Your code can deadlock because you're not making any guarantee that the Cell threads will actually be in the wait() block at the time that notifyAll occurs. The following is one sequence of events that could cause this problem:
可爱的例子!这不是死锁,因为根据定义,只有当一个线程同时持有多个锁,而另一个线程尝试以不同的顺序获取相同的锁时,才会发生死锁。
我怀疑这里的问题是由 Cell 对象中发生的虚假唤醒引起的(如果在模拟对象中发生虚假唤醒,则不会产生任何影响,因为在循环中调用 wait() 会导致等待需重新输入)。
单元中的虚假唤醒将导致额外的减量。这反过来将使测试
while(counter.getValue() != 0)
被跳过。将该条件更改为 while(counter.getValue() >= 0) ,“死锁”就会消失。如果有效,请告诉我们。
Lovely example! It isn't deadlock, since by definition that can only occur when a thread holds more than one lock simultaneously, and another tries to acquire the same locks in a different order.
I suspect that the problem here is caused by spurious wake-ups occurring in the Cell objects (if a spurious wake-up occured in the Simulation object it would have no effect as the wait() is called in a loop which will cause the wait to be re-entered).
A spurious wake-up in a Cell will cause an extra decrement. This in turn will make the test
while(counter.getValue() != 0)
to be stepped over.Change that condition to
while(counter.getValue() >= 0)
and the 'deadlocks should disappear. Please let us know if it works.这不是僵局。您的主线程可能会错过计数器上的通知,并且在计数器已为 0 后将卡在 counter.wait() 上。使用 jstack JDK 工具来分析线程在这种情况下正在做什么。
This is not a deadlock. Your main thread can miss a notify on the counter and will be stuck on counter.wait() after it is alread at 0. Use jstack JDK tool to analyze what threads are doing in a situation like this.