Java wait() notify() 方法最后一个线程永远等待

Java wait() notify() methods last thread waiting forever

我在玩Java同步机制,遇到了我无法解释的情况。

在下面的代码示例中,我有一个简单的应用程序,它正在生成一些线程,这些线程正在等待来自对象的信号并在收到通知后相互通知:

public class Main {
    public static final Object o = new Object();

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 6; ++i) {
            new Thread(() -> {
                try {
                    synchronized (o) {
                        o.wait();
                        System.out.println("Some work");
                        o.notify();
                    }
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName() + " notified someone");
            }).start();
        }
        synchronized (o) {
            o.notify();
        }
    }
}

大多数时候应用程序最后创建的线程挂起: 响应是:

Some work
Some work
Some work
Some work
Some work
Thread-3 notified someone
Thread-4 notified someone
Thread-0 notified someone
Thread-2 notified someone
Thread-1 notified someone

在应用试错技术后,我发现如果我在主线程应用程序的循环和通知之间暂停 Thread.sleep(100);,应用程序就会开始正常工作。

经过调查,我发现从 main 方法发送通知后线程的状态是:

main "Thread-0" prio=5 Id=13 RUNNABLE
main "Thread-1" prio=5 Id=14 WAITING on java.lang.Object@2133c8f8 owned by "Thread-0" Id=13
main "Thread-2" prio=5 Id=15 WAITING on java.lang.Object@2133c8f8 owned by "Thread-0" Id=13
main "Thread-3" prio=5 Id=16 WAITING on java.lang.Object@2133c8f8 owned by "Thread-0" Id=13
main "Thread-4" prio=5 Id=17 WAITING on java.lang.Object@2133c8f8 owned by "Thread-0" Id=13
main "Thread-5" prio=5 Id=18 BLOCKED on java.lang.Object@2133c8f8 owned by "Thread-0" Id=13

就在发送最后一个通知之前,状态是:

Thread-4 "Thread-4" prio=5 Id=17 RUNNABLE
Thread-4 "Thread-5" prio=5 Id=18 BLOCKED on java.lang.Object@2133c8f8 owned by "Thread-4" Id=17

// Final state

"Thread-5" prio=5 Id=18 WAITING on java.lang.Object@2133c8f8

很明显最后一个线程从app一开始就被阻塞,无法接收到倒数第二个线程的通知,最后到了临界区代码,一直在waiting line等待通知。

可能它最初被阻止是因为当他们都试图获得 o 锁时与 main 方法发生冲突。

如果有人更了解这种多线程的东西给我一些解释或一些关于这种行为的提示,那就太好了。

问题出在没有其他线程在等待时调用通知。信号量和作业队列突出显示您缺少的内容

信号量

public class Semaphore {
  private int tickets;
  public Semaphore (int tickets) {
    this.tickets = ticket;
  }

  public synchronized void getTicket() {
    if (tickets > 0) {
      tickets--;
    } else {
      while (tickets == 0) {
        wait();
      }
      tickets--;
    }
  }

  public synchronized void releaseTicket() {
    tickets++;
    notify();
  }
}

作业队列的工作方式类似,但它有一个 Runnables 队列而不是工单。当线程调用 getJob 时,如果队列中有作业,它会将该作业从队列中取出并将其交给线程。否则,线程等待作业。

您注意到等待进入同步块的线程 (BLOCKED) 和在 wait() 调用时阻塞的线程 (WAITING) 之间的区别。

您的 loop.You 中存在竞争条件,请创建您的线程并启动它们。他们中的大多数将进入同步块。这些线程中的每一个都进入一个同步块,获取锁,并在等待时释放它。

在您的主线程进入同步块之前,最后一个线程没有时间 运行 同步块。一旦你的 main 获得了你的对象的锁,通知一个等待锁的线程。该线程唤醒并获取锁,而不让最后一个线程进入同步块。

前五个线程完成并通知,一个线程不通知任何人。然后最后一个线程有机会进入synchronized块调用wait,但是永远得不到通知。

这就是 Thread.sleep 有帮助的原因,因为您的最后一个线程将有时间到达同步块并等待。