我已经阅读了很多这方面的帖子。这个答案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>
答案 0 :(得分:10)
sleep()
不会受到虚假唤醒的影响。它用于睡眠的低级系统调用可能是,但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()
来电。
答案 1 :(得分:1)
通过重新测试谓词来检测虚假唤醒。
如果没有理由进行唤醒,则唤醒是假的。如果其他一些故意故意唤醒你,那么唤醒并不是虚假的。
或者,更简单地说:如果你等待的东西没有发生,那么唤醒是虚假的。如果你正在等待的事情发生了,那么唤醒并不是虚假的。你必须能够检查你正在等待或发生的事情 - 否则,你怎么知道你必须在第一时间等待它?
所以在一个线程故意唤醒你之前,让它设置一个你醒来时检查的同步变量。如果设置了该变量,那么唤醒不是假的,你清除标志。如果没有设置,则唤醒是spuroius,你通常会忽略唤醒。
因此,流程如下:
在线程唤醒/发出信号/中断:
等待事情发生: