当前位置: 首页 > news >正文

thread.join 是干什么的?原理是什么?

Thread.join


加了join,表示join的线程的修改对于join之外的代码是可见的。
代码示例:

public class JoinDemo {private static int i = 1000;public static void main(String[] args) {new Thread(()->{i = 3000;}).start();System.out.println("i="+i);}
}

我们在main线程中定义一个i,初始值为1000。
在main方法中创建一个线程,将i值设置为3000,启动线程。
然后main方法打印 i 的值。
请问,i 值为多少?

我们执行后的输出结果为:

i=1000

这是由于main线程先于thread线程,所以输出的i 值为1000。
如果我们希望 Thread线程中的执行结果对main线程可见,怎么办?
使用 thread.join(),如下所示:

public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{i = 3000;});thread.start();thread.join();System.out.println("i="+i);
}

执行结果:

i=3000

所以说,如果我们希望结果是可见的话,可以通过join来做。
那么join的实现原理是什么?请看下图:
在这里插入图片描述
main线程中,创建了一个线程t1;
t1.start() 启动线程;
t1线程修改了i的值为3000;而且让修改可见。说明t1线程阻塞的main线程,使它无法打印。
t1线程继续执行直到终止。
t1终止后,去唤醒被它阻塞的线程。main线程继续执行,打印出 i=3000

有阻塞,就一定要唤醒,否则线程无法释放。我们是怎么触发唤醒的?
我们进入join的源码:

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()) {long delay = millis - now;if (delay <= 0) {break;}wait(delay);now = System.currentTimeMillis() - base;}}}

join是个synchronized的方法,里面有个 wait(0) 方法来阻塞。
那它是如何唤醒的呢?
我们去看hotspot的源码,其中的thread.cpp:

// For any new cleanup additions, please check to see if they need to be applied to
// cleanup_failed_attach_current_thread as well.
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {assert(this == JavaThread::current(),  "thread consistency check");HandleMark hm(this);Handle uncaught_exception(this, this->pending_exception());this->clear_pending_exception();Handle threadObj(this, this->threadObj());assert(threadObj.not_null(), "Java thread object should be created");if (get_thread_profiler() != NULL) {get_thread_profiler()->disengage();ResourceMark rm;get_thread_profiler()->print(get_thread_name());}// FIXIT: This code should be moved into else part, when reliable 1.2/1.3 check is in place{EXCEPTION_MARK;CLEAR_PENDING_EXCEPTION;}// FIXIT: The is_null check is only so it works better on JDK1.2 VM's. This// has to be fixed by a runtime query methodif (!destroy_vm || JDK_Version::is_jdk12x_version()) {// JSR-166: change call from from ThreadGroup.uncaughtException to// java.lang.Thread.dispatchUncaughtExceptionif (uncaught_exception.not_null()) {Handle group(this, java_lang_Thread::threadGroup(threadObj()));{EXCEPTION_MARK;// Check if the method Thread.dispatchUncaughtException() exists. If so// call it.  Otherwise we have an older library without the JSR-166 changes,// so call ThreadGroup.uncaughtException()KlassHandle recvrKlass(THREAD, threadObj->klass());CallInfo callinfo;KlassHandle thread_klass(THREAD, SystemDictionary::Thread_klass());LinkResolver::resolve_virtual_call(callinfo, threadObj, recvrKlass, thread_klass,vmSymbols::dispatchUncaughtException_name(),vmSymbols::throwable_void_signature(),KlassHandle(), false, false, THREAD);CLEAR_PENDING_EXCEPTION;methodHandle method = callinfo.selected_method();if (method.not_null()) {JavaValue result(T_VOID);JavaCalls::call_virtual(&result,threadObj, thread_klass,vmSymbols::dispatchUncaughtException_name(),vmSymbols::throwable_void_signature(),uncaught_exception,THREAD);} else {KlassHandle thread_group(THREAD, SystemDictionary::ThreadGroup_klass());JavaValue result(T_VOID);JavaCalls::call_virtual(&result,group, thread_group,vmSymbols::uncaughtException_name(),vmSymbols::thread_throwable_void_signature(),threadObj,           // Arg 1uncaught_exception,  // Arg 2THREAD);}if (HAS_PENDING_EXCEPTION) {ResourceMark rm(this);jio_fprintf(defaultStream::error_stream(),"\nException: %s thrown from the UncaughtExceptionHandler"" in thread \"%s\"\n",pending_exception()->klass()->external_name(),get_thread_name());CLEAR_PENDING_EXCEPTION;}}}// Called before the java thread exit since we want to read info// from java_lang_Thread objectEventThreadEnd event;if (event.should_commit()) {event.set_javalangthread(java_lang_Thread::thread_id(this->threadObj()));event.commit();}// Call after last event on threadEVENT_THREAD_EXIT(this);// Call Thread.exit(). We try 3 times in case we got another Thread.stop during// the execution of the method. If that is not enough, then we don't really care. Thread.stop// is deprecated anyhow.if (!is_Compiler_thread()) {int count = 3;while (java_lang_Thread::threadGroup(threadObj()) != NULL && (count-- > 0)) {EXCEPTION_MARK;JavaValue result(T_VOID);KlassHandle thread_klass(THREAD, SystemDictionary::Thread_klass());JavaCalls::call_virtual(&result,threadObj, thread_klass,vmSymbols::exit_method_name(),vmSymbols::void_method_signature(),THREAD);CLEAR_PENDING_EXCEPTION;}}// notify JVMTIif (JvmtiExport::should_post_thread_life()) {JvmtiExport::post_thread_end(this);}// We have notified the agents that we are exiting, before we go on,// we must check for a pending external suspend request and honor it// in order to not surprise the thread that made the suspend request.while (true) {{MutexLockerEx ml(SR_lock(), Mutex::_no_safepoint_check_flag);if (!is_external_suspend()) {set_terminated(_thread_exiting);ThreadService::current_thread_exiting(this);break;}// Implied else:// Things get a little tricky here. We have a pending external// suspend request, but we are holding the SR_lock so we// can't just self-suspend. So we temporarily drop the lock// and then self-suspend.}ThreadBlockInVM tbivm(this);java_suspend_self();// We're done with this suspend request, but we have to loop around// and check again. Eventually we will get SR_lock without a pending// external suspend request and will be able to mark ourselves as// exiting.}// no more external suspends are allowed at this point} else {// before_exit() has already posted JVMTI THREAD_END events}// Notify waiters on thread object. This has to be done after exit() is called// on the thread (if the thread is the last thread in a daemon ThreadGroup the// group should have the destroyed bit set before waiters are notified).ensure_join(this);assert(!this->has_pending_exception(), "ensure_join should have cleared");// 6282335 JNI DetachCurrentThread spec states that all Java monitors// held by this thread must be released.  A detach operation must only// get here if there are no Java frames on the stack.  Therefore, any// owned monitors at this point MUST be JNI-acquired monitors which are// pre-inflated and in the monitor cache.//// ensure_join() ignores IllegalThreadStateExceptions, and so does this.if (exit_type == jni_detach && JNIDetachReleasesMonitors) {assert(!this->has_last_Java_frame(), "detaching with Java frames?");ObjectSynchronizer::release_monitors_owned_by_thread(this);assert(!this->has_pending_exception(), "release_monitors should have cleared");}// These things needs to be done while we are still a Java Thread. Make sure that thread// is in a consistent state, in case GC happensassert(_privileged_stack_top == NULL, "must be NULL when we get here");if (active_handles() != NULL) {JNIHandleBlock* block = active_handles();set_active_handles(NULL);JNIHandleBlock::release_block(block);}if (free_handle_block() != NULL) {JNIHandleBlock* block = free_handle_block();set_free_handle_block(NULL);JNIHandleBlock::release_block(block);}// These have to be removed while this is still a valid thread.remove_stack_guard_pages();if (UseTLAB) {tlab().make_parsable(true);  // retire TLAB}if (JvmtiEnv::environments_might_exist()) {JvmtiExport::cleanup_thread(this);}// We must flush any deferred card marks before removing a thread from// the list of active threads.Universe::heap()->flush_deferred_store_barrier(this);assert(deferred_card_mark().is_empty(), "Should have been flushed");#if INCLUDE_ALL_GCS// We must flush the G1-related buffers before removing a thread// from the list of active threads. We must do this after any deferred// card marks have been flushed (above) so that any entries that are// added to the thread's dirty card queue as a result are not lost.if (UseG1GC) {flush_barrier_queues();}
#endif // INCLUDE_ALL_GCS// Remove from list of active threads list, and notify VM thread if we are the last non-daemon threadThreads::remove(this);
}

线程终止的时候,会调用JavaThread::exit 方法,这个退出方法中,有个清理的工作:

  // Notify waiters on thread object. This has to be done after exit() is called// on the thread (if the thread is the last thread in a daemon ThreadGroup the// group should have the destroyed bit set before waiters are notified).ensure_join(this);

唤醒当前线程对象上的阻塞线程,这是在调用exit之后被完成的。
我们来看 ensure_join 方法具体做了些什么?

static void ensure_join(JavaThread* thread) {// We do not need to grap the Threads_lock, since we are operating on ourself.Handle threadObj(thread, thread->threadObj());assert(threadObj.not_null(), "java thread object must exist");ObjectLocker lock(threadObj, thread);// Ignore pending exception (ThreadDeath), since we are exiting anywaythread->clear_pending_exception();// Thread is exiting. So set thread_status field in  java.lang.Thread class to TERMINATED.java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);// Clear the native thread instance - this makes isAlive return false and allows the join()// to complete once we've done the notify_all belowjava_lang_Thread::set_thread(threadObj(), NULL);lock.notify_all(thread);// Ignore pending exception (ThreadDeath), since we are exiting anywaythread->clear_pending_exception();
}

其中关键的是 lock.notify_all(thread); wait方法锁的是当前t1实例,t1退出的时候,拿到t1实例,然后拿到t1的锁,然后notify_all。

以上就是我们对thread.join()的全部解读。

相关文章:

thread.join 是干什么的?原理是什么?

Thread.join 加了join&#xff0c;表示join的线程的修改对于join之外的代码是可见的。 代码示例&#xff1a; public class JoinDemo {private static int i 1000;public static void main(String[] args) {new Thread(()->{i 3000;}).start();System.out.println("…...

论文阅读 | Cross-Attention Transformer for Video Interpolation

前言&#xff1a;ACCV2022wrokshop用transformer做插帧的文章&#xff0c;q&#xff0c;kv&#xff0c;来自不同的图像 代码&#xff1a;【here】 Cross-Attention Transformer for Video Interpolation 引言 传统的插帧方法多用光流&#xff0c;但是光流的局限性在于 第一&…...

【C++修炼之路】22.哈希

每一个不曾起舞的日子都是对生命的辜负 哈希一.哈希概念及性质1.1 哈希概念1.2 哈希冲突1.3 哈希函数二.哈希冲突解决2.1 闭散列/开放定址法2.2 开散列/哈希桶三.开放定址法代码3.1 插入Insert3.2 查找Find3.3 删除Erase3.4 映射的改良&完整代码四.开散列代码4.1 插入Inser…...

HashMap原理(一):哈希函数的设计

目录导航哈希函数的作用与本质哈希函数设计哈希表初始容量的校正哈希表容量为2的整数次幂的缺陷及解决办法注&#xff1a;为了简化代码&#xff0c;提高语义&#xff0c;本文将HashMap很多核心代码抽出并根据代码含义为代码片段取名&#xff0c;完全是为了方便读者理解。哈希函…...

06--WXS 脚本

1、简介WXS&#xff08;WeiXin Script&#xff09;是小程序的一套脚本语言&#xff0c;结合 WXML &#xff0c;可以构建出页面的结构。 注意事项WXS 不依赖于运行时的基础库版本&#xff0c;可以在所有版本的小程序中运行。WXS 与 JavaScript 是不同的语言&#xff0c;有自己的…...

【Vue3】vue3 + ts 封装城市选择组件

城市选择-基本功能 能够封装城市选择组件&#xff0c;并且完成基础的显示隐藏的交互功能 &#xff08;1&#xff09;封装通用组件src/components/city/index.vue <script lang"ts" setup name"City"></script> <template><div class…...

C语言if判断语句的三种用法

C if 语句 一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。 语法 C 语言中 if 语句的语法&#xff1a; if(boolean_expression) {/* 如果布尔表达式为真将执行的语句 */ }如果布尔表达式为 true&#xff0c;则 if 语句内的代码块将被执行。如果布尔表达式为 false&…...

React中echarts的封装

做大屏的时候经常会遇到 echarts 展示 在 React &#xff08;^18.2.0&#xff09; 中对 echarts &#xff08;^5.4.0&#xff09; 的简单封装 echarts 封装使用 props 说明 参数说明类型可选值默认值opts初始化传入的 opts https://echarts.apache.org/zh/api.html#echarts…...

IV测试系统3A太阳能模拟器在光伏中应用

一、概述IV测试系统3A太阳能模拟器应具备光束准直、光斑均匀、辐照稳定、且与太阳光谱匹配的特点&#xff0c;使用户可足不出户的完成需要太阳光照条件的测试。科迎法电气提供多规格高品质的太阳模拟器&#xff0c;可适用于单晶硅、多晶硅、非晶硅、染料敏化、有机、钙钛矿等各…...

Vue 中过滤器 filter 使用教程

Vue 过滤器 filter 使用教程文章目录Vue 过滤器 filter 使用教程一、过滤器1.1 过滤器使用的背景1.2 格式化时间的不同实现1.3 过滤器的使用1.4 过滤器总结一、过滤器 1.1 过滤器使用的背景 过滤器提供给我们的一种数据处理方式。过滤器功能不是必须要使用的&#xff0c;因为它…...

源码numpy笔记

参考文章 numpy学习 numpy中的浅复制和深复制的详细用法 numpy中的np.where torch.gather() Numpy的核心数据结构&#xff0c;就叫做array就是数组&#xff0c;array对象可以是一维数组&#xff0c;也可以是多维数组 array本身的属性 shape&#xff1a;返回一个元组&#xf…...

【VUE】六 路由和传值

目录 一、 路由和传值 二、案例 三、案例存在无法刷新问题 一、 路由和传值 当某个组件可以根据某些参数值的不同&#xff0c;展示不同效果时&#xff0c;需要用到动态路由。 例如&#xff1a;访问网站看到课程列表&#xff0c;点击某个课程&#xff0c;就可以跳转到课程详…...

ChatGPT修炼指南和它的电力畅想

近期&#xff0c;ChatGPT刷屏各大社交平台&#xff0c;无疑成为人工智能界最靓的仔&#xff01; 身为一款“会说话”的聊天机器人程序&#xff0c;它与前辈产品Siri、小度、微软小冰等有什么不同&#xff1f;先来听听小伙伴们怎么说。 ChatGPT何以修炼得这么强大&#xff1f;…...

基于vscode开发vue项目的详细步骤教程

1、Vue下载安装步骤的详细教程(亲测有效) 1_水w的博客-CSDN博客 2、Vue下载安装步骤的详细教程(亲测有效) 2 安装与创建默认项目_水w的博客-CSDN博客 目录 五、vscode集成npm开发vue项目 1、vscode安装所需要的插件&#xff1a; 2、搭建一个vue小页面(入门vue) 3、大致理解…...

【C++初阶】1. C++入门

1. 前言 1. 什么是C C语言是结构化和模块化的语言&#xff0c;适合处理较小规模的程序。对于复杂的问题&#xff0c;规模较大的程序&#xff0c;需要高度的抽象和建模时&#xff0c;C语言则不合适。为了解决软件危机&#xff0c; 20世纪80年代&#xff0c; 计算机界提出了OOP(…...

数据结构与算法(二十)快速排序、堆排序(四)

数据结构与算法&#xff08;三&#xff09;软件设计(十九)https://blog.csdn.net/ke1ying/article/details/129252205 排序 分为 稳定排序 和 不稳定排序 内排序 和 外排序 内排序指在内存里&#xff0c;外排序指在外部存储空间排序 1、排序的方法分类。 插入排序&#xff…...

TensorRT量化工具pytorch_quantization代码解析(二)

有些地方看的不是透彻&#xff0c;后续继续补充&#xff01; 继续看张量量化函数&#xff0c;代码位于&#xff1a;tools\pytorch-quantization\pytorch_quantization\tensor_quant.py ScaledQuantDescriptor 量化的支持描述符:描述张量应该如何量化。QuantDescriptor和张量…...

buu [BJDCTF2020]easyrsa 1

题目描述 &#xff1a; from Crypto.Util.number import getPrime,bytes_to_long from sympy import Derivative from fractions import Fraction from secret import flagpgetPrime(1024) qgetPrime(1024) e65537 np*q zFraction(1,Derivative(arctan(p),p))-Fraction(1,Deri…...

taobao.user.openuid.getbyorder( 根据订单获取买家openuid )

&#xffe5;免费不需用户授权 根据订单获取买家openuid&#xff0c;最大查询30个 公共参数 请求地址: HTTP地址 http://gw.api.taobao.com/router/rest 公共请求参数: 请求示例 TaobaoClient client new DefaultTaobaoClient(url, appkey, secret); UserOpenuidGetbyorderR…...

Mac iTerm2 rz sz

1、安装brew&#xff08;找了很多&#x1f517;&#xff0c;就这个博主的好用&#xff09; Mac如何安装brew&#xff1f;_行走的码农00的博客-CSDN博客_mac brew 2、安装lrzsz brew install lrzsz 检查是否安装成功 brew list 定位lrzsz的安装目录 brew list lrzsz 执…...

新手避坑指南:用MATLAB复现TI IWR1443雷达的距离与速度FFT处理(附完整代码)

新手避坑指南&#xff1a;用MATLAB复现TI IWR1443雷达的距离与速度FFT处理&#xff08;附完整代码&#xff09; 第一次拿到IWR1443毫米波雷达开发板时&#xff0c;看着官方文档里密密麻麻的英文术语和零散的代码片段&#xff0c;我对着电脑屏幕发呆了整整半小时。作为电子工程专…...

开源像素艺术工具推荐:Pixel Fashion Atelier vs Automatic1111定制化对比

开源像素艺术工具推荐&#xff1a;Pixel Fashion Atelier vs Automatic1111定制化对比 1. 工具概览 1.1 Pixel Fashion Atelier简介 Pixel Fashion Atelier是一款基于Stable Diffusion与Anything-v5的图像生成工作站。它采用独特的复古日系RPG界面设计&#xff0c;将AI图像生…...

UE4SS终极指南:解锁虚幻引擎4/5游戏Mod开发新境界

UE4SS终极指南&#xff1a;解锁虚幻引擎4/5游戏Mod开发新境界 【免费下载链接】RE-UE4SS Injectable LUA scripting system, SDK generator, live property editor and other dumping utilities for UE4/5 games 项目地址: https://gitcode.com/gh_mirrors/re/RE-UE4SS …...

【Python工业视觉性能跃迁指南】:3大编译优化+5个CUDA加速技巧,让检测速度提升8.7倍

第一章&#xff1a;Python工业视觉性能跃迁的底层逻辑与评估体系Python在工业视觉领域长期面临“高表达性”与“低实时性”的根本矛盾。性能跃迁并非单纯依赖硬件升级或框架切换&#xff0c;而源于对计算图编译、内存布局优化、异构加速调度及IO瓶颈解耦四维协同机制的系统性重…...

DeepFaceLab 512分辨率遮罩模型实战:如何精准处理头发和手部细节(附下载)

DeepFaceLab 512分辨率遮罩模型实战&#xff1a;如何精准处理头发和手部细节 在数字内容创作领域&#xff0c;视频换脸技术已经从简单的娱乐工具逐渐演变为影视特效、虚拟偶像制作等专业场景的核心技术。对于DeepFaceLab的中高级用户来说&#xff0c;如何突破基础换脸的局限&am…...

从if-else到assign:聊聊RTL代码风格如何影响X态传播与电路质量

从if-else到assign&#xff1a;RTL代码风格对X态传播与电路质量的深层影响 在数字IC设计领域&#xff0c;X态就像电路中的"幽灵信号"&#xff0c;它无声无息地潜伏在设计中&#xff0c;直到某个关键时刻突然显现&#xff0c;引发难以追踪的异常行为。对于RTL工程师而…...

Mac能够连接校园网,但是无法上网

Mac电脑能够正常连接校园网&#xff0c;但是无法上网解决步骤&#xff1a;打开系统设置&#xff0c;网络&#xff0c;WI-FI&#xff0c;DNS把现有的删掉重置它。原因分析&#xff1a;应该是在使用代理时、访问什么网站被自动篡改了 DNS 设置&#xff0c;导致连接的 DNS 无法解析…...

基于MATLAB的VSG逆变器无源性分析与稳定性研究

基于MATLAB的VSG逆变器无源性分析与稳定性研究 摘要 随着分布式发电和微电网技术的快速发展,逆变器作为新能源并网的关键接口,其稳定性问题日益突出。虚拟同步发电机(VSG)控制技术通过模拟同步发电机的机电特性,为逆变器提供惯性和阻尼支撑,成为提升系统稳定性的重要手…...

RTX 4090D 24G镜像一文详解:PyTorch 2.8预装xFormers/FlashAttention-2实战

RTX 4090D 24G镜像一文详解&#xff1a;PyTorch 2.8预装xFormers/FlashAttention-2实战 1. 镜像概述与核心优势 PyTorch 2.8深度学习镜像为RTX 4090D 24GB显卡量身打造&#xff0c;经过CUDA 12.4深度优化&#xff0c;提供开箱即用的高性能计算环境。这个镜像特别适合需要处理…...

【CPython 3.13无锁并发白皮书】:全球首批实测团队披露的4类典型崩溃场景与修复参数

第一章&#xff1a;Python 无锁 GIL 环境下的并发模型配置概览Python 的全局解释器锁&#xff08;GIL&#xff09;本质上限制了 CPython 中多线程对 CPU 密集型任务的并行执行能力。然而&#xff0c;“无锁 GIL 环境”并非指 GIL 被移除&#xff0c;而是指通过绕过 GIL 依赖的并…...