java - 如何检测虚假唤醒

标签 java linux multithreading pthreads posix

我看过很多关于这个的帖子。本回答https://stackoverflow.com/a/6701060/2359945通过建议测试中断标志获得 100 赏金。

我测试过这个,它对我不起作用。所以,问题仍然存在,我如何检测虚假唤醒,或者这是不可能的?谢谢。

class TestSpuriousWakeup {
  static Thread t1, tInterrupt, tNotify;

  // spawn one thread that will be interrupted and be notified
  // spawn one thread that will interrupt
  // spawn one thread that will notify
  public static void main(String[] args) {
    System.out.println("*** main Starting");
    initThreads();

    try {
      t1.start();

      Thread.sleep(2000);
      tNotify.start();
      tNotify.join();

      Thread.sleep(2000);
      tInterrupt.start();
      tInterrupt.join();

      t1.join();
    } catch (InterruptedException e) {
      System.out.println("*** Unexpected interrupt in main");
    }

    System.out.println("*** main Ended.");
  }

  private static void initThreads() {
    t1 = new Thread() {
      @Override
      public void run() {
        System.out.println("ThreadInterruptMe Started ...");
        boolean stop = false;
        Thread.interrupted(); // clear the interrupted flag
        while (!stop) {
          try {
            System.out.println("ThreadInterruptMe Sleeping 5000ms ...");
            Thread.sleep(5000);
          } catch (InterruptedException e) {
            System.out.println("ThreadInterruptMe InterruptedException e!");
            System.out.println("ThreadInterruptMe e.getCause => " + e.getCause());
            System.out.println("ThreadInterruptMe e.getLocalizedMessage => " + e.getLocalizedMessage());
            stop = Thread.interrupted();

            if (stop) {
              System.out.println("ThreadInterruptMe was INTERRUPTED because Thread.interrupted() is true"); // never happens
            } else {
              System.out.println("ThreadInterruptMe was NOTIFIED because Thread.interrupted() is false"); // always happens
            }
          } finally {
            Thread.interrupted(); // clear the interrupted flag
            System.out.println("ThreadInterruptMe InterruptedException finally");
          }
        }
        System.out.println("ThreadInterruptMe Ended.");
      }
    };

    tInterrupt = new Thread() {
      @Override
      public void run() {
        System.out.println("  ThreadInterruptYou Started ... interrupting now!");
        t1.interrupt();
        System.out.println("  ThreadInterruptYou Ended.");
      }
    };

    tNotify = new Thread() {
      @Override
      public void run() {
        System.out.println("    ThreadNotifyYou Started ... notifying now!");
        t1.interrupt();
        System.out.println("    ThreadNotifyYou Ended.");
      }
    };
  }
}

输出:

*** main Starting
ThreadInterruptMe Started ...
ThreadInterruptMe Sleeping 5000ms ...
    ThreadNotifyYou Started ... notifying now!
ThreadInterruptMe InterruptedException e!
    ThreadNotifyYou Ended.
ThreadInterruptMe e.getCause => null
ThreadInterruptMe e.getLocalizedMessage => sleep interrupted
ThreadInterruptMe was NOTIFIED because Thread.interrupted() is false
ThreadInterruptMe InterruptedException finally
ThreadInterruptMe Sleeping 5000ms ...
  ThreadInterruptYou Started ... interrupting now!
ThreadInterruptMe InterruptedException e!
ThreadInterruptMe e.getCause => null
ThreadInterruptMe e.getLocalizedMessage => sleep interrupted
ThreadInterruptMe was NOTIFIED because Thread.interrupted() is false
ThreadInterruptMe InterruptedException finally
ThreadInterruptMe Sleeping 5000ms ...
  ThreadInterruptYou Ended.
ThreadInterruptMe InterruptedException finally
ThreadInterruptMe Sleeping 5000ms ...
ThreadInterruptMe InterruptedException finally
ThreadInterruptMe Sleeping 5000ms ...

<infinite loop>

最佳答案

哪些虚假唤醒不是

sleep() 不受虚假唤醒的影响。它用于 hibernate 的低级系统调用可能是,但 Java 会为您处理这个细节,如果它被过早唤醒,则重新进入系统调用。作为用户,您不会受到虚假唤醒的影响。

虚假唤醒也与线程中断无关。那是一个单独的工具。线程永远不会“虚假地中断”。如果您的线程被中断,则意味着有人在您的线程上调用了 Thread.interrupt()。找到那个代码,你就会找到罪魁祸首。

什么是虚假唤醒

如果您想测试虚假唤醒,请改为使用 Object.wait() 运行测试,因为这是遭受虚假唤醒影响的经典方法。

使用 wait() 的简单方法是简单地调用它,期望它仅在其他线程调用 notify() 时返回。例如,消息发送循环可能是:

for (;;) {
    synchronized (monitor) {
        if (queue.isEmpty()) {  // incorrect
            monitor.wait();
        }
    }

    send(queue.remove());
}

如果 wait() 在没有将消息添加到队列的情况下虚假地唤醒,这将失败。解决方案是在 wait() 周围添加一个循环,以便在每次线程被唤醒时验证条件。

for (;;) {
    synchronized (monitor) {
        while (queue.isEmpty()) {  // correct
            monitor.wait();
        }
    }

    send(queue.remove());
}

然后,模拟虚假唤醒的最简单方法是简单地调用notify()而不更改循环条件

synchronized (monitor) {
    monitor.notify();
}

这将对执行 wait() 的线程产生完全相同的效果,就好像它遇到虚假唤醒一样。使用 if 的错误代码不会意识到队列仍然是空的并且会崩溃。使用 while 的正确代码将重新检查条件并安全地重新进入 wait() 调用。

关于java - 如何检测虚假唤醒,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30675085/

相关文章:

java - Spring @Repository 异常翻译

java - 从 res/raw 文件夹中读取文本文件

java - 从 InputStream 解压缩文件并返回另一个 InputStream

c++ - 类内信号处理

php - 如何 grep 目录中 "$_POST"或 "$_GET"和 "include"的所有文件

c - 如何创建未定义数量的线程并在 Windows 上的 c 中使用 WaitForMultipleObjects()

java - 如何从 JavaScript 中的 div 中获取值并对其进行排序?

linux - 如何在 linux 中基于 tcp 选项字段丢弃数据包

java - 在java中产生太多线程

c# - 如何强制所有程序线程在程序关闭时退出?