Thread.join()

2023-05-16

3.1 用法

下源码里对这个方法的描述。

// Thread.java

/**
 * Waits for this thread to die.
 *
 * <p> An invocation of this method behaves in exactly the same
 * way as the invocation
 *
 * <blockquote>
 * {@linkplain #join(long) join}{@code (0)}
 * </blockquote>
 *
 * @throws  InterruptedException
 *          if any thread has interrupted the current thread. The
 *          <i>interrupted status</i> of the current thread is
 *          cleared when this exception is thrown.
 */
public final void join() throws InterruptedException {
    join(0);
}

复制代码

源码里对这个方法的描述只有简单的一句话“等待这个线程的消亡”,也就说一个线程在调用另一个线程的join方法后就要等待这个线程消亡后才能继续往下执行,相当于把并发的线程在这个时间点变成串行执行序列了。

在理解了这点后,再回过头来看看上面的题目,在thread1thread2的死锁等待方面的分析都是正确的,关键点在于主线程在这之后是否还可以继续往下执行。由于在主线程中调用了thread1.join()thread2.join(),就表明主线程必须等待这两个线程执行完才能继续执行,但thread1thread2已经处于死锁状态,是不可能消亡的,这也就导致主线程无法继续下去了,所以最后的输出结果应该是:

thread1 start
thread2 start
复制代码

我自己也在回来之后运行过这段代码,结果和分析的一致,也算弄明白了Thread.join()是咋回事了。

3.2 实现原理

在弄明白Thread.join()的用法和含义是不是就圆满结束了?当然不是,我们尽可能地了解其内部的实现原理。

简单来说就是要知道两个问题:

  1. 如何让当前线程在调用Thread.join()之后停止执行,直到另一个线程消亡的?
  2. 在另一个线程消亡后,当前线程是如何继续开始执行的?

3.2.1 如何停止

一切来源于代码,我们自然要到代码去寻找答案,还是再来看下Thread.join()的声明和定义:

// Thread.java

/**
 * Waits for this thread to die.
 */
public final void join() throws InterruptedException {
    // 直接调用另一个重载函数。
    join(0);
}
    
/**
 * Waits at most {@code millis} milliseconds for this thread to
 * die. A timeout of {@code 0} means to wait forever.
 *
 * <p> This implementation uses a loop of {@code this.wait} calls
 * conditioned on {@code this.isAlive}. As a thread terminates the
 * {@code this.notifyAll} method is invoked. It is recommended that
 * applications not use {@code wait}, {@code notify}, or
 * {@code notifyAll} on {@code Thread} instances.
 *
 */
public final synchronized void join(long millis)
throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
            // 当线程还处于存活状态时,就一直等待。
            wait(0);
        }
    } else {
        while (isAlive()) {
            // 等待时间没有直接使用参数指定的 millis,原因是为了保持退出循环的可能。
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            // 当线程还处于存活状态时,就等待一段时间。
            wait(delay);
            // 更新 now 时间信息,是为了等待时间结束后,再次进到这个循环时能够由于 delay <= 0 而直接退出循环。
            now = System.currentTimeMillis() - base;
        }
    }
}
复制代码

这个函数的代码量并不大,逻辑也比较容易理解,就是在线程A中调用线程B的join()方法后,这个线程A就会处于对线程B的wait状态,根据传入的参数不同可以处于一直等待也可以只等待一段时间。

3.2.2 如何恢复

既然线程A在调用线程B的join方法后就会处于wait状态,那线程A又是在何时恢复执行的呢?这里只介绍不带参数的join方法,即一直等待的情况。从join方法的介绍中可知,要等到线程B的消亡,线程A才能恢复,这是如何实现的呢?

// Thread.java

/**
 * This method is called by the system to give a Thread
 * a chance to clean up before it actually exits.
 */
private void exit() {
    if (group != null) {
        // 调用销毁回调
        group.threadTerminated(this);
        group = null;
    }
    /* Aggressively null out all reference fields: see bug 4006245 */
    target = null;
    /* Speed the release of some of these resources */
    threadLocals = null;
    inheritableThreadLocals = null;
    inheritedAccessControlContext = null;
    blocker = null;
    uncaughtExceptionHandler = null;
}
复制代码

在线程真正退出之前,系统会调用exit方法来进行一些回收操作,从代码可以看到除了group.threadTerminated()之外都是一些置空操作,很可能起到恢复作用的逻辑就藏在group.threadTerminated()里面,这里的groupThreadGroup的实例,是线程在初始化的时候创建的,可以简单理解为这个线程属于这类线程组的。

直接来看ThreadGroup.threadTerminated()的代码:

/**
 * Notifies the group that the thread {@code t} has terminated.
 *
 * <p> Destroy the group if all of the following conditions are
 * true: this is a daemon thread group; there are no more alive
 * or unstarted threads in the group; there are no subgroups in
 * this thread group.
 *
 * @param  t
 *         the Thread that has terminated
 */
void threadTerminated(Thread t) {
    synchronized (this) {
        remove(t);

        if (nthreads == 0) {
            // 唤醒所有的等待线程。
            notifyAll();
        }
        if (daemon && (nthreads == 0) &&
            (nUnstartedThreads == 0) && (ngroups == 0))
        {
            destroy();
        }
    }
}
复制代码

很明显,在线程被销毁的时候会调用notifyAll()来唤醒所有等待线程,所以线程A才能在线程B消亡的时候恢复运行。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Thread.join() 的相关文章

  • MySQL 加入不存在的地方

    我有一个连接两个表的 MySQL 查询 Voters 家庭 他们加入voters household id and household id 现在我需要做的是修改它 将选民表连接到第三个表 称为消除 voter id and elimina
  • 使用 ActiveRecord 和 Rails 3 进行复杂 JOIN

    我有以下型号 class User lt ActiveRecord Base has many memberships has many groups through gt memberships end class Group lt Ac
  • R dplyr left_join 错误

    所以我一直在尝试使用 left join 将新数据集的列获取到我的主数据集 称为员工 我已经仔细检查了矢量名称和我没有进行的清理 但似乎没有任何效果 这是我的代码 将不胜感激任何帮助 job codes lt read csv Quest
  • 选择子子项中的最新记录

    我有以下表格 列 Parent ParentID Child ChildID ParentID SubChild SubChildID ChildID Date Parent与 具有一对多关系Child Child与 具有一对多关系SubC
  • 根据最近的关键条件连接 Spark DataFrame

    在 PySpark 中执行模糊连接的高效方法是什么 我正在寻找社区对在最近的关键条件下加入大型 Spark DataFrame 的可扩展方法的看法 请允许我通过一个有代表性的例子来说明这个问题 假设我们有以下 Spark DataFrame
  • postgresql自连接

    假设我有一张这样的桌子 id device cmd value id unique row ID device device identifier mac address cmd some arbitrary command value v
  • 当我加入第二个表时总和不正确

    这是我第一次请求你的帮助 实际上我必须创建一个查询 并为其做了一个类似的示例 我有两张桌子 Report ReportID Date headCount Production ProdID ReportID Quantity 我的问题是使用
  • JPA Criteria API 任意数量的联接/子查询

    我需要使用以下实体构建相交类型查询 为了清楚起见 减少了实体 Entity and other stuff public class Member Id private Long id private String name Entity
  • 连接两个表并保存到第三个sql

    我想加入两张桌子 TableA wordA primarykey countA abc 25 abcd 29 abcde 45 TableB wordB primarykey countB ab
  • 将内部联接和 where 子句添加到 INSERT INTO ON DUPLICATE KEY UPDATE

    我从 INSERT INTO ON DUPLICATE KEY UPDATE MySQL 语句开始 INSERT INTO Table1 field1 field2 VALUES 1 2 ON DUPLICATE KEY UPDATE fi
  • 非等值连接一步添加 data.table 中范围表的所有列

    我确信我忽略了显而易见的事情 但我找不到一种方法来连接 查找 表的所有列data table非等值连接一步到位 我看了阿伦的演讲 https github com Rdatatable data table wiki talks ArunS
  • SQL:列出多个连接语句中的重复记录?

    你好 以下查询在连接多个表后返回所有员工 select e from dbo EMP e join dbo HREMP a on a ID e ID join dbo LOGO c on c EMPID e id join dbo LOGO
  • SQL:使用相等的键和最近的键进行连接(类似于 Pandas 的合并)

    例如 我有2个这样的表 对于表 1 中的每一行 我想获取该行 same customer id and nearest date 就我而言 table2 date lt table1 date 结果应该是这样的 我怎样才能在 SQL 中做到
  • 动态/条件 SQL 连接?

    我在 MSSQL 表 TableB 中有数据 其中 dbo tableB myColumn 在特定日期后更改格式 我正在做一个简单的连接到该表 Select dbo tableB theColumnINeed from dbo tableA
  • 删除重复的 SQL 记录以允许唯一键

    我在 MYSQL 数据库中有一个表 销售 该表理应强制执行唯一约束以防止重复 事实证明 首先删除欺骗并设置约束有点棘手 表结构 简化 id 唯一 autoinc 产品编号 目标是强制product id 的唯一性 我想要应用的重复数据删除策
  • sql join 告诉我 ID 是否存在于其他表中

    我有 2 张桌子 A B ID FKID 1 3 2 3 3 4 4 4 我需要一个 select 语句 它显示 A 的所有内容 其中一个字段告诉我表 B 是否有任何与该 ID 匹配的 id Desired Result ID hasB 1
  • ORA-00933 与内部联接和“as”混淆

    我有一个使用以下命令从两个表中获取数据的查询inner join 但我收到错误SQL command not properly ended as 下面有一个星号 select P carrier id O order id O aircra
  • 简单的 Linq 查询对同一个表有重复的连接?

    来自 Julia Lerman 的新实体框架书中的示例 我有一个包含两个表的数据库 联系人和地址 Contact 表有一个 ContactID int 以及名字 姓氏等 Address 表有一个 ContactID 以及城市 州 邮政编码等
  • 连接 3 三张表

    我有这个图表应该可以解释我的情况 我需要一些关于连接 3 个表的帮助 我不知道如何做这种事情 因此 我可以通过执行以下操作来经历一段检索记录的 while 循环 img src alt Album AlbumID 使用内部联接 http w
  • 连接两个表而不返回不需要的行

    我的表结构如下所示 tbl users tbl issues userid real name issueid assignedid creatorid 1 test 1 1 1 1 2 test 2 2 1

随机推荐

  • java设计模式-命令模式

    18 xff0c 命令模式 18 1 命令模式的定义和特点 命令 xff08 Command xff09 模式的定义如下 xff1a 将一个请求封装为一个对象 xff0c 使发出请求的责任和执行请求的责任分割开 这样两者之间通过命令对象进行
  • java设计模式-代理模式

    17 xff0c 代理模式 17 1 代理模式的定义和特点 代理模式的定义 xff1a 由于某些原因需要给某对象提供一个代理以控制对该对象的访问 这时 xff0c 访问对象不适合或者不能直接引用目标对象 xff0c 代理对象作为访问对象和目
  • 工厂方法模式

    概念定义 工厂方法 Factory Method 模式 xff0c 又称多态工厂 Polymorphic Factory 模式或虚拟构造器 Virtual Constructor 模式 工厂方法模式通过定义工厂抽象父类 或接口 负责定义创建
  • TextFuseNet: Scene Text Detection with Richer Fused Features论文阅读

    TextFuseNet Scene Text Detection with Richer Fused Features 利用更丰富的特征融合进行场景文本检测 代码 xff1a https github com ying09 TextFuse
  • JUC原子类: CAS, Unsafe和原子类详解

    CAS 线程安全的实现方法包含 互斥同步 synchronized 和 ReentrantLock非阻塞同步 CAS AtomicXXXX无同步方案 栈封闭 xff0c Thread Local xff0c 可重入代码 什么是CAS CAS
  • OKHttp中的责任链模式

    一 什么是责任链模式 责任链 xff0c 顾名思义是将多个节点通过链条的方式连接起来 xff0c 每一个节点相当于一个对象 xff0c 而每一个对象层层相关 xff0c 直接或者间接引用下一个对象 xff08 节点 xff09 xff1b
  • android bugly关于混淆后如何知道正确代码

    bugly xff1a 腾讯自制 xff0c 是个4 xff0c 5句代码就能简单加入在线更新 捕获异常的好功能 xff0c 后台也是使用腾讯的 Android混淆 xff1a 启用一个配置 xff0c 把所有变量 类名改成 34 a 34
  • 大康Dacom Athlete+蓝牙耳机与手机配对上的原因及解决办法:

    1 原因 xff1a 蓝牙耳机没有进入配对模式 解决办法 xff1a 蓝牙耳机都有一个功能键 xff0c 长按听到开机提示音后不要松手 xff0c 继续长按 xff0c 直至听到进入配对模式提示音或者 滴 的提示音 xff0c 此时蓝红等交
  • android查看编译后的class文件

    其查看目录如下 然后在硬盘文件中打开 xff0c 可以看到详细的class文件列表
  • socket的shutdownInput和shutdownOutput

    虽然在大多数的时候可以直接使用Socket类或输入输出流的close方法关闭网络连接 xff0c 但有时我们只希望关闭OutputStream或InputStream xff0c 而在关闭输入输出流的同时 xff0c 并不关闭网络连接 这就
  • 使用广播接收器时,onReceive 会多次执行

    原因一 xff1a 没有在onDestory中调用解注册 unregisterReceiver 原因二 xff1a BroadcastReceiver变量所在的Activity或者Fragment被创建的多次 xff0c 形成多个对象
  • Android Studio自动生成单例代码

    AS中有可以自己设置代码模板 xff0c 使用起来简单方便 同样的 xff0c 单例类的代码样式统一 xff0c 除了类名外全部一致 所以使用模板更加方便 在设置中的Editor Live Template中新建模板 xff0c 然后把单例
  • android:excludeFromRecents 属性需要注意的小地方

    在 Android 系统中 xff0c 如果我们不想某个 Activity 出现在 Recent screens 中 xff0c 可以设置 lt activity gt 属性 android excludeFromRecents 为 tru
  • G.711编码原理

    目录 参考概述G 711原理总结 1 参考 1 wikipedia A law algorithm 2 github com quatanium foscam ios sdk 3 charybdis G711算法学习 2 概述 本文目的 x
  • RxJava 之Consumer和Action的使用

    在之前的RxJava中已经讲到创建观察者的代码如下 xff1a 创建观察者 Observer lt String gt observer 61 new Observer lt String gt 64 Override public voi
  • JAVA中枚举如何保证线程安全

    枚举类型到底是什么类呢 xff1f 是enum吗 xff1f 明显不是 xff0c enum就和class一样 xff0c 只是一个关键字 xff0c 他并不是一个类 xff0c 那么枚举是由什么类维护的呢 xff0c 首先写一个简单的枚举
  • Activity的启动流程

    总的流程图 xff1a 1 进程A与AMS的交互过程 此处以跨进程启动Activity分析一下源码流程 xff1a A调用startActivity时 xff0c 需要与AMS交互 xff0c 此时需要需要获取到AMS的代理对象Binder
  • Handler同步屏障

    一 消息机制之同步屏障 消息机制的同步屏障 xff0c 其实就是阻碍同步消息 xff0c 只让异步消息通过 而开启同步屏障的方法就是调用下面的方法 xff1a MessageQueue postSyncBarrier 源码如下 xff1a
  • view的绘制流程

    一 view树的绘制流程 measure gt layout gt draw measure 1 ViewGroup LayoutParams 指定部件的长宽 2 MeasureSpec 32位的int值 前两位代表模式 后30位测量规格的
  • Thread.join()

    3 1 用法 下源码里对这个方法的描述 Thread java Waits for this thread to die lt p gt An invocation of this method behaves in exactly the