我正在研究java中的等待(长时间超时),在官方文档中我发现了以下描述:
- 其他某个线程调用该对象的notify方法,而线程T恰好被任意选择为要唤醒的线程。
- 其他一些线程调用该对象的notifyAll 方法。
- 其他一些线程中断线程 T。
- 或多或少已经过了指定的实时时间。然而,如果超时为零,则不考虑实时性,线程只是等待直到收到通知。
最后一项说The specified amount of real time has elapsed, more or less,所以在我的选择中,如果我们调用wait(time),当时间流逝时,线程应该唤醒自己。
所以我写了下面的代码进行测试。
public static void testTimedWait() {
Object lock = new Object();
DateTimeFormatter df = DateTimeFormatter.ofPattern("HH:mm:ss:SSS");
new Thread(() -> {
synchronized (lock) {
try {
System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " start to run");
lock.wait(5_000);
System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " finished running");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "thread-1").start();
new Thread(() -> {
synchronized (lock) {
try {
System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " start to run");
Thread.sleep(10_000);
//lock.notifyAll();
System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " finished running");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "thread-2").start();
}
Run Code Online (Sandbox Code Playgroud)
在Thread-1I invokelock.wait(5_000)和 in Thread-2I invoke中Thread.sleep(10_000),因此一旦代码开始运行,Thread-1应该在 5 秒后唤醒,Thread-2理论上应该在 10 秒后唤醒。
当我们运行上面的代码时,结果如下所示:
Thread-15秒后没有醒来!
有人可以帮助理解为什么等待时间过去后线程没有唤醒,提前致谢!

更新1:
更改代码如下:
public static void testTimedWait() {
Object lock = new Object();
DateTimeFormatter df = DateTimeFormatter.ofPattern("HH:mm:ss:SSS");
new Thread(() -> {
synchronized (lock) {
try {
System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " start to run");
lock.wait(20_000);
System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " finished running");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "thread-1").start();
new Thread(() -> {
synchronized (lock) {
try {
System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " start to run");
Thread.sleep(10_000);
//lock.notifyAll();
System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " finished running");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "thread-2").start();
}
Run Code Online (Sandbox Code Playgroud)
上面的代码让Thread-1等待 20 秒并Thread-2睡眠 10 秒,Thread-1将在特定秒数后唤醒。现在它按预期工作。
更新2:删除Thread-2
public static void testTimedWait() {
Object lock = new Object();
DateTimeFormatter df = DateTimeFormatter.ofPattern("HH:mm:ss:SSS");
new Thread(() -> {
synchronized (lock) {
try {
System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " start to run");
lock.wait(20_000);
System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " finished running");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "thread-1").start();
}
Run Code Online (Sandbox Code Playgroud)
测试结果也按预期工作。但我仍然不知道为什么Thread-1等待时间更长时没有醒来Thread-2
您的线程在同一个对象上同步。因此,必须注意methods\xe2\x80\x99s对同步的影响:
\nwait一旦它重新获得了对该对象的控制,该对象上的所有同步声明都会恢复到原来的状态 - 即恢复到调用该方法时的情况。\xe2\x80\x9dThread.sleep(long): \xe2\x80\x9c该线程不会失去任何监视器的所有权。\xe2\x80\x9d
因此,当第一个线程调用 时wait,它会释放锁,这是第二个线程进入synchronized块的唯一方法。然后,第二个线程调用sleep,这不会释放锁。因此,在时间过去后,第一个线程无法继续,因为它无法获取锁,而第二个线程仍然持有。
当然,如果你延长第一个线程\xe2\x80\x99s的等待时间,比第二个线程等待的时间更长,此时锁已经被释放,等待时间过后第一个线程可以立即继续。
\n