【问题标题】:Both Threads stuck on wait condition in Java两个线程都停留在Java中的等待状态
【发布时间】:2020-05-13 05:32:18
【问题描述】:

我有一个服务器和两个客户端,服务器启动两个线程(ServerHandler),它们都通过相应客户端的Socket TCP连接开始连接到服务器。

预期行为:
一个 ServerHandler 线程向客户端发送消息,而另一个 ServerHandler 线程等待wait() ...然后工作线程通知睡眠线程并等待...等等。

实际行为:
两个 ServerHandlers 同时等待。他们都进入了不应该出现的同步块,一个线程应该在等待,而另一个线程工作。

ServerHandler 的代码 sn-p(它的两个实例正在运行)

private static Object lock = new Object();
...
@Override
public void run() {
    System.out.println(String.format("  --> Server handler: %s is in run method...", serverID));
    while (true) {
        synchronized (lock){
            while (!Server.isFinished()) {
                try {
                    System.out.println(String.format("  --> Server handler: %s is waiting...", serverID));
                    lock.wait();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            System.out.println(String.format("  --> Server handler: %s is ready to send board...", serverID));
            Server.setFinished(true);
            sendBoard();

            notify();
        }
    }
}

注意: Server 类启动两个 ServerHandler 线程。 finished 默认设置为 false

输出:

CLIENT connected!
<-- I'M Alan I want to play!
--> Server handler 1 instantiated!
CLIENT connected!
<-- I'M Bot I want to play!
--> Server handler 2 instantiated!
HANDLER started...
HANDLER started...
--> Server handler: 1 is in run method...
--> Server handler: 2 is in run method...
--> Server handler: 1 is waiting...
--> Server handler: 2 is waiting...

预期行为:

CLIENT connected!
<-- I'M Bot I want to play!
--> Server handler 1 instantiated!
CLIENT connected!
<-- I'M Alan I want to play!
--> Server handler 2 instantiated!
HANDLER started...
HANDLER started...
--> Server handler: 1 is in run method...
--> Server handler: 1 is waiting...
--> Server handler: 2 is in run method...
--> Server handler: 2 is ready to send board...
...

谢谢!

【问题讨论】:

  • 所以你有 ServerHandler 实例 s1 和 s2; s1 调用 s1.wait(),s2 调用 s2.wait()。谁调用 s1.notify() ?
  • 我以为第一次访问同步块会阻止它,实际上会发送板我的错是什么?
  • 第一个进入同步块的线程发现Server.isFinished()=false,调用wait()。 (见docs.oracle.com/javase/7/docs/api/java/lang/Object.html#wait())。所以这个线程进入等待状态(它被暂停),并且释放监视器。下一个线程获取监视器并进入等待状态。
  • 虽然实际上 - 他们打电话给synchronized(this)?在这种情况下,每个服务器处理程序都在自己的监视器上进行侦听(并且只能被唤醒,但只能通知该监视器)。
  • 我现在在服务器处理程序中创建了一个静态对象锁,并且我在这个对象上进行了同步。但仍然是同样的问题。将设置完成的变量。在等待解决方案之前?

标签: java multithreading synchronized


【解决方案1】:

您必须在 lock 对象上调用 notify

【讨论】:

  • 我确实尝试过lock.notify();,但没有任何改变
【解决方案2】:

不确定您在此处使用的同步机制是否正确。

你的流程如下

  1. 线程 1 抓取监视器
  2. 线程 1 检查服务器状态
  3. 服务器未准备好
  4. 线程 1 丢弃监视器并等待
  5. 线程 2 抓取监视器
  6. 线程 2 检查服务器状态
  7. 服务器未准备好
  8. 线程 2 丢弃监视器并等待
  9. 死锁,因为两个线程都在等待对方

因此线程永远无法通过服务器检查,因为没有人会通知他们可以继续。为此,您需要线程 1 或线程 2 在两者都准备好时通知另一个,从而导致死锁。

您可能需要考虑使用 Cyclic Barrier 代替。这将让您等到收集到足够的线程来开始游戏。

private static Object lock = new Object();
private static CyclicBarrier barrier = new CyclicBarrier(2, () -> setReady());
...
@Override
public void run() {
    System.out.println(String.format("  --> Server handler: %s is in run method...", serverID));
    while (true) {
        synchronized (lock){
            while (!Server.isFinished()) {
                try {
                    System.out.println(String.format("  --> Server handler: %s is waiting...", serverID));
                    lock.wait();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
           }
           System.out.println(String.format("  --> Server handler: %s is ready to send board...", serverID));

            sendBoard();

            notify();
        }
    }
}

public static void setReady() {
    synchronized (lock) {
        try {
            sendBoard();
            System.out.println(String.format("  --> Server handler: %s is waiting...", serverID));
            lock.notify();
            lock.wait();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

这样的事情可以让你改变阻塞,直到两个线程都启动,第二个线程到达屏障然后触发 setReady 方法发送板,然后等待另一个线程。

【讨论】:

  • 这是否会导致一个线程播放,而另一个等待,然后工作线程进入睡眠状态,而正在睡眠的线程进入工作模式(等等)这就是我想要实现的目标吗?
  • 不,这只能解决获得足够多的玩家,这似乎是个问题。让我用一些设置来更新答案。
  • 我只是希望他们不要同时玩...这就是为什么我总是必须锁定一个线程而另一个线程工作
  • 更新了阻塞屏障。虽然不确定这是最佳选择,但您可能只需要一个更简单的互斥锁/信号量来阻止第一个线程然后移动到下一个线程。
【解决方案3】:

您能否提供一些示例说明您的预期输出是什么?

一个看似偶然的错误是两个线程都有自己的锁,而不是共享一个(注意 static)。

另一件没有意义的事情是在您已经拥有的锁上调用notify

private static Object lock = new Object();

public void run() {
    while (true) {
        synchronized (lock) {
            while (!Server.isFinished()) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }

            Server.setFinished(true);
            sendBoard();
        }
    }
}

【讨论】:

  • 我编辑了我的问题(参见预期行为)当我将它们锁定在静态对象上时(如您所写),线程“Thread-0”java.lang.IllegalMonitorStateException 中的异常被抛出(在@ 987654323@声明)
  • 你能再试一次吗?仅当您在不拥有的锁上调用 wait() 时才会引发异常。在这种情况下,lock.wait() 显然在 synchronized (lock) 块内被调用,因此必须拥有。
  • 我试了 5 次,总是 IllegaMonitor... 例外
  • 您确定您已将synchronized (this) 更改为synchronized (lock)
  • 更新:我忘了写 lock.wait() 异常不再存在,但线程仍然同时等待
猜你喜欢
  • 2013-01-17
  • 2018-01-17
  • 2016-06-02
  • 2014-04-03
  • 1970-01-01
  • 2020-02-26
  • 2017-05-29
  • 1970-01-01
  • 2014-01-31
相关资源
最近更新 更多