同步语句第二次不起作用

Synchronized statement doesn't work the second time

更新: 结果证明我的 Synchronized 语句没有任何问题。我早些时候在代码中犯了一个错误,它在那里阻塞了。对于浪费的时间,我深表歉意,并感谢您的所有帮助。

PS。 @sh0rug0ru 建议的 CountDownLatch 更适合我的需要,也正是我需要的。

上一个:

这是代码的相关部分:

synchronized (readyLock) { //sets this thread's status to ready
readyLock.notify();
System.out.println("notify works");
try {
    readyLock.wait();
    System.out.println("woke up from sleep");
} catch (InterruptedException ex) {
    Logger.getLogger(Battleship_server_clientThread.class.getName()).log(Level.SEVERE, null, ex);
}
readyLock.notify();
}
while (true) { //game loop             

//Some stuff here        

    synchronized (readyLock) { //sets this thread's status to ready
        readyLock.notify();
        System.out.println("notify works");
        try {
            readyLock.wait();
            System.out.println("woke up from sleep");
        } catch (InterruptedException ex) {
            Logger.getLogger(Battleship_server_clientThread.class.getName()).log(Level.SEVERE, null, ex);
        }
        readyLock.notify();
    }
//Some more stuff here
}

readyLock对象在另一个class中初始化为static final变量:

static final Object readyLock = new Object();

Synchronized 语句第一次起作用,就像在第一个同步语句块中一样。但是 while 循环中的 synchronized 块不起作用。这两个地方的代码完全相同。

此 class 是一个线程,来自服务器 运行 两次。这是一个两人游戏。其余代码为 here.

我想弄清楚为什么第二个同步语句不起作用。我认为它只是出于某种原因阻塞在那里。

注意所有 System.out.println("");行用于调试。

当您使用 notify() 时,状态会发生变化。

当您 wait() 时,您应该检查状态变化。

否则你有风险;

  • 如果那一刻没有等待,notify 就会丢失。
  • wait 可以虚假唤醒。

这些方法的文档中对此进行了介绍。

不要使用 notify,请使用 notifyAllnotify 将随机选择一个等待线程。如果该线程返回到 wait 状态并且 notify 不再被调用,所有其他等待的线程将继续等待,您自己就会陷入死锁。

使用 notifyAll 将为处于 wait 状态的所有线程提供继续的机会。

notify 非常危险,几乎没有充分的理由使用它。

更好的是,您为什么首先使用 wait/notify/notifyAll?你想达到什么目的?无论您尝试实现哪种并发模式,都可能已经在 java.util.concurrent 中实现了,它将更清晰、更安全地执行您尝试执行的任何操作。

正如我在评论中所说,每个同步块中的第二个 notify() 是可疑的。如果 wait()ing 的目的是让您描述的两个线程轮流运行,以便在任何给定时间只有一个线程运行,那么您的代码就被破坏了。以下是实际发生的情况:

  1. 一个线程进入(其中一个)同步块,排除所有其他线程。
  2. 该线程通知另一个阻塞等待 readyLock,如果有的话,这样它可以在当前线程释放该监视器时继续。
  3. 当前线程阻塞,释放监视器并等待它自己的通知。
  4. 另一个线程执行它的操作,直到它到达相同或相似的块,此时它通知正在等待的线程,然后它自己开始等待。
  5. 原线程继续执行,立即通知等待线程。
  6. 原线程退出同步块,释放readyLock的监视器
  7. 然后第二个线程可以 return 来自 wait(),运行 与第一个并发。

这本身并不能解释死锁,但如果您不希望这两个线程 运行 同时发生,那么问题可能会随之而来。

以下是您可以如何更好地构建该场景:

synchronized (readyLock) {
    setMyTurn(false);
    otherPlayer.setMyTurn(true);
    readyLock.notify();
    while (!isMyTurn()) {
        try {
            readyLock.wait();
        } catch (InterruptedException ex) {
            Logger.getLogger(Battleship_server_clientThread.class.getName()).log(
                Level.SEVERE, null, ex);
        }
    }
}

请注意,您可能需要考虑 notifyAll() 而不是 notify()。如果只能有一个其他线程在监视器上等待,这种区别并不重要,但如果所有潜在的等待者都使用循环和条件检查,那么它会更安全,如图所示。