如何检测虚假唤醒

Raz*_*azl 1 java linux multithreading posix pthreads

我已经阅读了很多这方面的帖子.这个答案/sf/answers/469074231/通过建议测试中断的标志声称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.");
      }
    };
  }
}
Run Code Online (Sandbox Code Playgroud)

输出:

*** 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>
Run Code Online (Sandbox Code Playgroud)

Joh*_*ica 10

什么虚假的唤醒不是

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

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

什么虚假唤醒

如果你想测试虚假的唤醒,请Object.wait()改为运行测试,因为这是经典的方法,它们会受到影响.

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

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

    send(queue.remove());
}
Run Code Online (Sandbox Code Playgroud)

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

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

    send(queue.remove());
}
Run Code Online (Sandbox Code Playgroud)

因此,模拟虚假唤醒的最简单方法是在不改变循环条件的情况下简单地调用notify().

synchronized (monitor) {
    monitor.notify();
}
Run Code Online (Sandbox Code Playgroud)

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