循环同步死锁

发布于 2024-10-13 12:11:12 字数 1869 浏览 6 评论 0原文

我在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

爱给你人给你 2024-10-20 12:11:12

首先,您可以使用 AtomicInteger 类而不是您创建的 Counter 类。 AtomicInteger 类是线程安全的,因此您可以使用原子操作,例如 decrementAndGetincrementAndGet

要实现等待每个单元线程完成的功能,您可以使用 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.

岁月如刀 2024-10-20 12:11:12

在您的代码中,似乎无法保证当执行 sync.notifyAll() 时,所有 Cell 线程都会进入 sync.wait()。这是指最后一个 Cell 线程(示例中的第五个)需要获取 sync 锁才能等待。但模拟线程也在尝试同样的事情,但没有确保每个人都在等待。这种竞争条件使得模拟有时会在最后一个单元能够执行相同操作并等待之前抢到锁。

由于最后一个 Cell 没有等待,因此它不会收到通知,因此整个事情陷入困境。
您可以通过添加 System.out.println() 作为每个同步 (sync) 块中的第一行并相应地写入“等待同步”和“通知同步”来测试这一点。当您通知它时,您将看到只有 4 个线程正在等待同步。

为了确保模拟器通知时每个人都在等待,请在 Cell#run() 中嵌套两个同步块:

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 static void main(String[] args) {
        new Simulation().start();
    }
}

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 (sync) {
                synchronized (counter) {
                    counter.decrement();
                    counter.notifyAll();
                }
                try {
                    sync.wait();
                } catch (Exception ignored) {}
            }


        }
    }
}

class Simulation extends Thread {

    public static final int THREAD_NUM = 900;
    public static final int ROUND_NUM = 30;

    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();
            }
        }
    }
}

In your code, there seems to be no guarantee that when sync.notifyAll() gets executed, all the Cell threads got to sync.wait(). This refers to the last Cell thread (the fifth in your example) that needs to grab the lock for sync 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:

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 static void main(String[] args) {
        new Simulation().start();
    }
}

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 (sync) {
                synchronized (counter) {
                    counter.decrement();
                    counter.notifyAll();
                }
                try {
                    sync.wait();
                } catch (Exception ignored) {}
            }


        }
    }
}

class Simulation extends Thread {

    public static final int THREAD_NUM = 900;
    public static final int ROUND_NUM = 30;

    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();
            }
        }
    }
}
我的影子我的梦 2024-10-20 12:11:12

您的代码可能会死锁,因为您无法保证在发生notifyAll 时Cell 线程实际上位于wait() 块中。以下是可能导致此问题的一系列事件:

  1. 模拟启动所有线程,并阻止等待 0 值。
  2. 每个线程依次调用decrement,然后counter.notifyAll,然后丢失自己的时间片
  3. 主线程已收到通知,醒来,发现计数器为0,调用sync.notifyAll,循环到顶部,无限等待。
  4. 序列中的每个线程都被赋予一个时间片,前进到 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:

  1. Simulation starts all threads, and blocks waiting for a 0 value.
  2. Each thread in sequence calls decrement, then counter.notifyAll, and then loses its time slice
  3. The main thread has been notified, wakes up, finds the counter is at 0, calls sync.notifyAll, loops to the top, and waits indefinitely.
  4. Each thread in sequence is given a time slice, advances to the wait(), and waits indefinitely.
霞映澄塘 2024-10-20 12:11:12

可爱的例子!这不是死锁,因为根据定义,只有当一个线程同时持有多个锁,而另一个线程尝试以不同的顺序获取相同的锁时,才会发生死锁。
我怀疑这里的问题是由 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.

沉睡月亮 2024-10-20 12:11:12

这不是僵局。您的主线程可能会错过计数器上的通知,并且在计数器已为 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文