首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >理解线程Java中的等待

理解线程Java中的等待
EN

Stack Overflow用户
提问于 2022-02-24 04:49:47
回答 2查看 1.3K关注 0票数 2

我有以下代码:-

代码语言:javascript
复制
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的答案。有两个线程,ThreadB和主线程,所以当我执行等待方法时,ThreadB应该按照定义等待,不应该添加值,因此0应该由主线程打印?

EN

回答 2

Stack Overflow用户

发布于 2022-02-24 05:18:25

每个对象都有一个内部锁,线程可以使用同步方式获取该锁。当线程无法执行任何操作时,线程调用等待,直到某些事情发生变化(例如,它可能试图插入到当前已满的有界队列中),它调用等待对象,该对象的锁是通过同步获得的。当主线程调用b.wait()时,这意味着主线程是处于休眠状态的线程。

当代码等待注释掉时,ThreadB线程仍在启动过程中,主线程可以接受锁,然后在ThreadB获得锁之前释放锁并打印总计,此时锁的总数仍为0。从技术上讲,这是一场比赛,并不能保证哪个线程是第一个线程,但是主线程有一个很好的领先优势。

当代码使用等待时,主线程获取ThreadB上的锁(再次领先于ThreadB),然后等待直到收到通知为止。当线程终止时,它会导致调度程序通知其等待集中的任何线程,所以当threadB完成时,主线程会唤醒并继续执行。由于ThreadB已经完成,到主线程开始打印它时,总数为10。

如果主线程在ThreadB之前没有得到锁,那么(因为ThreadB在它运行的整个过程中一直持有锁),直到ThreadB完成之后,它才能获得锁。这意味着,在即将死亡的线程发送通知时,它不在等待中,而是会在稍后等待,并且不会有任何通知来唤醒它并将其挂起。

这种问题--导致丢失通知和线程挂起的竞赛--可能在等待/通知被滥用时发生。有关使用等待和通知的正确方式,请阅读https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

如果您特别希望一个线程等待另一个线程完成,thread有一个名为join的实例方法,用于该方法,主线程将在这里调用它,如下所示

代码语言:javascript
复制
b.join();

顺便说一下,终止通知的行为记录在用于java.lang.Thread#join的api文档中,它说:

此实现使用以this.wait为条件的this.isAlive调用的循环。当线程终止时,将调用this.notifyAll方法。建议应用程序不要对线程实例使用等待、通知或notifyAll。

注意对线程对象进行同步的警告。它不能很好地处理JDK代码,比如锁定线程的join代码。

票数 3
EN

Stack Overflow用户

发布于 2022-02-28 01:03:01

我很困惑为什么我总是得到10的答案。有两个线程,ThreadB和主线程,所以当我执行等待方法时,ThreadB应该按照定义等待,不应该添加值,因此0应该由主线程打印?

由于Java线程终止的方式,您得到了一个结果。当Thread完成时,会通知Thread对象本身。因此,wait()方法会导致线程等待后台线程的终止。join()方法是通过调用wait()实现的,您应该直接使用join()而不是wait()

如果对等待行进行注释,则输出始终为0。

如果您没有wait()行,那么主线程很可能会在b线程启动之前完成并得到b的值。启动线程需要一些时间,您需要使用b.join()来确保等待b线程完成,并与其他线程所做的任何内存更改同步,在本例中为b.total

如果您放置一个睡眠而不是等待,那么即使0线程已经将其设置为10,您仍然可能会看到它的值,因为没有同步内存的东西,并且可能缓存0的b.total值。join()方法等待线程完成并同步内存,以便您能够看到线程的结果。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71247244

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档