我有以下代码:-
class ThreadB extends Thread {
int total;
@Override public void run() {
synchronized (this){
for(int i=0; i<5; i++){
total+=i;
}
}
}
}
public class Solution {
public static void main(String[] args) throws InterruptedException {
ThreadB b = new ThreadB();
b.start();
synchronized (b){
b.wait();
}
System.out.println(b.total);
}
}
每当我运行这个程序时,我得到的输出都是 10。 如果我注释等待线,我得到的输出始终为 0。
我很困惑为什么我得到的答案总是 10。 有 2 个线程,ThreadB 和主线程,因此当我执行 wait 方法时,ThreadB 应该根据定义等待,并且不应添加值,因此主线程应该打印 0?
最佳答案
每个对象都有一个内部锁,线程可以使用同步来获取该锁。当线程无能为力时调用 wait,直到发生变化(例如, 它可能试图插入到当前已满的有界队列中),它对通过synchronized 获取其锁的对象调用 wait 。当主线程调用b.wait()时,意味着主线程进入 hibernate 状态。
当代码中的wait被注释掉时,ThreadB线程仍在启动过程中,主线程可以获得锁,然后在ThreadB获得锁之前释放锁并打印total,此时total仍为0 . 从技术上讲,存在竞争,并且不能保证哪个线程先执行,但主线程有一个良好的开始。
当代码使用 wait 时,主线程将获取 ThreadB 上的锁(再次先于 ThreadB 到达那里),然后等待,直到收到通知。当线程终止时,它会导致调度程序通知其等待集中的任何线程,因此当 threadB 完成时,它会导致主线程唤醒并从那里继续。由于 ThreadB 已完成,当主线程开始打印它时,总数已达到 10。
如果主线程没有在 ThreadB 之前获得锁,那么(因为 ThreadB 在其运行的整个过程中都持有该锁)它在 ThreadB 完成之前无法获取锁。这意味着当垂死的线程发送通知时它不在等待集中,但会稍后等待,并且不会有任何通知唤醒它并且它会挂起。
这种问题(导致通知丢失和线程挂起的竞争)在错误使用 wait/notify 时可能会发生。有关使用等待和通知的正确方法,请阅读 https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html .
如果您特别希望一个线程等待另一个线程完成,Thread 有一个名为 join 的实例方法用于此目的,主线程将在此处调用该方法,如下所示
b.join();
顺便说一句,终止通知行为记录在 the api doc for java.lang.Thread#join 中,它说:
This implementation uses a loop of this.wait calls conditioned on this.isAlive. As a thread terminates the this.notifyAll method is invoked. It is recommended that applications not use wait, notify, or notifyAll on Thread instances.
请注意针对 Thread 对象进行同步的警告。它与 JDK 代码(例如锁定线程的 join)配合不佳。
关于java - 了解 Java 线程中的等待,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71247244/