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,表示join的线程的修改对于join之外的代码是可见的。 代码示例: 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
前言:ACCV2022wrokshop用transformer做插帧的文章,q,kv,来自不同的图像 代码:【here】 Cross-Attention Transformer for Video Interpolation 引言 传统的插帧方法多用光流,但是光流的局限性在于 第一&…...
【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的整数次幂的缺陷及解决办法注:为了简化代码,提高语义,本文将HashMap很多核心代码抽出并根据代码含义为代码片段取名,完全是为了方便读者理解。哈希函…...
06--WXS 脚本
1、简介WXS(WeiXin Script)是小程序的一套脚本语言,结合 WXML ,可以构建出页面的结构。 注意事项WXS 不依赖于运行时的基础库版本,可以在所有版本的小程序中运行。WXS 与 JavaScript 是不同的语言,有自己的…...
【Vue3】vue3 + ts 封装城市选择组件
城市选择-基本功能 能够封装城市选择组件,并且完成基础的显示隐藏的交互功能 (1)封装通用组件src/components/city/index.vue <script lang"ts" setup name"City"></script> <template><div class…...
C语言if判断语句的三种用法
C if 语句 一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。 语法 C 语言中 if 语句的语法: if(boolean_expression) {/* 如果布尔表达式为真将执行的语句 */ }如果布尔表达式为 true,则 if 语句内的代码块将被执行。如果布尔表达式为 false&…...
React中echarts的封装
做大屏的时候经常会遇到 echarts 展示 在 React (^18.2.0) 中对 echarts (^5.4.0) 的简单封装 echarts 封装使用 props 说明 参数说明类型可选值默认值opts初始化传入的 opts https://echarts.apache.org/zh/api.html#echarts…...
IV测试系统3A太阳能模拟器在光伏中应用
一、概述IV测试系统3A太阳能模拟器应具备光束准直、光斑均匀、辐照稳定、且与太阳光谱匹配的特点,使用户可足不出户的完成需要太阳光照条件的测试。科迎法电气提供多规格高品质的太阳模拟器,可适用于单晶硅、多晶硅、非晶硅、染料敏化、有机、钙钛矿等各…...
Vue 中过滤器 filter 使用教程
Vue 过滤器 filter 使用教程文章目录Vue 过滤器 filter 使用教程一、过滤器1.1 过滤器使用的背景1.2 格式化时间的不同实现1.3 过滤器的使用1.4 过滤器总结一、过滤器 1.1 过滤器使用的背景 过滤器提供给我们的一种数据处理方式。过滤器功能不是必须要使用的,因为它…...
源码numpy笔记
参考文章 numpy学习 numpy中的浅复制和深复制的详细用法 numpy中的np.where torch.gather() Numpy的核心数据结构,就叫做array就是数组,array对象可以是一维数组,也可以是多维数组 array本身的属性 shape:返回一个元组…...
【VUE】六 路由和传值
目录 一、 路由和传值 二、案例 三、案例存在无法刷新问题 一、 路由和传值 当某个组件可以根据某些参数值的不同,展示不同效果时,需要用到动态路由。 例如:访问网站看到课程列表,点击某个课程,就可以跳转到课程详…...
ChatGPT修炼指南和它的电力畅想
近期,ChatGPT刷屏各大社交平台,无疑成为人工智能界最靓的仔! 身为一款“会说话”的聊天机器人程序,它与前辈产品Siri、小度、微软小冰等有什么不同?先来听听小伙伴们怎么说。 ChatGPT何以修炼得这么强大?…...
基于vscode开发vue项目的详细步骤教程
1、Vue下载安装步骤的详细教程(亲测有效) 1_水w的博客-CSDN博客 2、Vue下载安装步骤的详细教程(亲测有效) 2 安装与创建默认项目_水w的博客-CSDN博客 目录 五、vscode集成npm开发vue项目 1、vscode安装所需要的插件: 2、搭建一个vue小页面(入门vue) 3、大致理解…...
【C++初阶】1. C++入门
1. 前言 1. 什么是C C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。为了解决软件危机, 20世纪80年代, 计算机界提出了OOP(…...
数据结构与算法(二十)快速排序、堆排序(四)
数据结构与算法(三)软件设计(十九)https://blog.csdn.net/ke1ying/article/details/129252205 排序 分为 稳定排序 和 不稳定排序 内排序 和 外排序 内排序指在内存里,外排序指在外部存储空间排序 1、排序的方法分类。 插入排序ÿ…...
TensorRT量化工具pytorch_quantization代码解析(二)
有些地方看的不是透彻,后续继续补充! 继续看张量量化函数,代码位于:tools\pytorch-quantization\pytorch_quantization\tensor_quant.py ScaledQuantDescriptor 量化的支持描述符:描述张量应该如何量化。QuantDescriptor和张量…...
buu [BJDCTF2020]easyrsa 1
题目描述 : 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 )
¥免费不需用户授权 根据订单获取买家openuid,最大查询30个 公共参数 请求地址: HTTP地址 http://gw.api.taobao.com/router/rest 公共请求参数: 请求示例 TaobaoClient client new DefaultTaobaoClient(url, appkey, secret); UserOpenuidGetbyorderR…...
Mac iTerm2 rz sz
1、安装brew(找了很多🔗,就这个博主的好用) Mac如何安装brew?_行走的码农00的博客-CSDN博客_mac brew 2、安装lrzsz brew install lrzsz 检查是否安装成功 brew list 定位lrzsz的安装目录 brew list lrzsz 执…...
高通平台开发系列讲解(Sensor篇)Gsensor基础知识
文章目录 一、什么是SENSOR?二、Sensor的分类及作用三、Gsensor的工作原理及介绍3.1、常见Gsensor3.2、Gsensor的特性沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇文章将介绍 Sensor 基础 一、什么是SENSOR? 传感器(英文名称:sensor )是一种检测装置,能感…...
图像处理实战--Opencv实现人像迁移
前言: Hello大家好,我是Dream。 今天来学习一下如何使用Opencv实现人像迁移,欢迎大家一起参与探讨交流~ 本文目录:一、实验要求二、实验环境三、实验原理及操作1.照片准备2.图像增强3.实现美颜功能4.背景虚化5.图像二值化处理6.人…...
OnlyOffice验证(二)在Centos7上部署OnlyOffice编译结果
在Centos7上部署OnlyOffice编译结果 此处将尝试将OnlyOffice验证(一)DocumentServer编译验证的结果部署到Centos7上。并且使用其它服务器现有的RabbitMq和Mysql。 安装Nginx 先安装Nginx需要的依赖环境: yum install openssl* -y yum insta…...
6.补充和总结【Java面试第三季】
6.补充和总结【Java面试第三季】前言推荐6.补充和总结69_总结闲聊回顾和总结继续学习最后前言 2023-2-4 19:08:01 以下内容源自 【尚硅谷Java大厂面试题第3季,跳槽必刷题目必扫技术盲点(周阳主讲)-哔哩哔哩】 仅供学习交流使用 推荐 Jav…...
基于ssm框架大学生社团管理系统(源码+数据库+文档)
一、项目简介 本项目是一套基于ssm框架大学生社团管理系统,主要针对计算机相关专业的正在做bishe的学生和需要项目实战练习的Java学习者。 包含:项目源码、数据库脚本等,该项目可以直接作为bishe使用。 项目都经过严格调试,确保可…...
vulnhub靶场NAPPING: 1.0.1教程
靶场搭建靶机下载地址:Napping: 1.0.1 ~ VulnHub直接解压双击ova文件即可使用软件:靶机VirtualBox,攻击机VMware攻击机:kali信息收集arp-scan -l上帝之眼直接来看看网站可以注册账号,那就先试试。注册完后登入哦。要输…...
Docker基本介绍
最近需要将项目做成一个web应用并部署到多台服务器上,于是就简单学习了一下docker,做一下小小的记录。 1、简单介绍一下docker 我们经常遇到这样一个问题,自己写的代码在自己的电脑上运行的很流畅,在其他人电脑上就各种bug&…...
可用于标记蛋白质216699-36-4,6-ROX,SE,6-羧基-X-罗丹明琥珀酰亚胺酯
一.6-ROX,SE产品描述:6-羧基-X-罗丹明琥珀酰亚胺酯(6-ROX,SE)是一种用于寡核苷酸标记和自动DNA测序的荧光染料,可用于标记蛋白质,寡核苷酸和其他含胺分子的伯胺(-NH2)。西…...
高数:极限的定义
目录 极限的定义: 数列极限的几何意义: 由极限的定义得出的极限的两个结论: 编辑 极限的第三个结论: 例题 方法1: 编辑 方法2: 编辑 方法3: 编辑 极限的定义: 如何理…...
大数据技术之Hadoop
第1章 Hadoop概述1.1 Hadoop是什么1.2 Hadoop发展历史(了解)1.3 Hadoop三大发行版本(了解)Hadoop三大发行版本:Apache、Cloudera、Hortonworks。Apache版本最原始(最基础)的版本,对于…...
wordpress 广告 插件下载/搜索排名怎么做
211 Calender常用方法 方法名 说明 public int get(int field) 返回给定日历的值 public abstract void add9int field,int amount) 根据日历的规则,将指定的时间量添加或减去给定的日历字段 public final void set(int year,int month,int date) 设置当前的…...
申请个人营业执照需要什么资料/深圳搜狗seo
自MySQL5.1.0起,增加了一个非常有特色的功能–事件调度器(Event Scheduler),可以用做定时执行某些特定任务(例如:删除记录、对数据进行汇总等等),来取代原先只能由操作系统的计划任务来执行的工作。更值得一…...
ghost wordpress比较/优化关键词排名提升
正则表达式(regex)的定义 正则表达式是一套特殊字符和格式组成的对字符串进行运算的表达式,用表达式对目标字符串从左到右依次匹配,达成检索、过滤、摘取的目的。 正则表达式的功能 判断用户输入内容是否符合要求。将用户输入的内容与预先定义的表达式比对,若不符合要求则提…...
哪个旅游网站做的最好/定制企业网站建设制作
前言 曾听过很多人说Android学习很简单,做个App就上手了,工作机会多,毕业后也比较容易找工作。这种观点可能是很多Android开发者最开始入行的原因之一。 在工作初期,工作主要是按照业务需求实现App页面的功能,按照设…...
平台做网站点击/想建立自己的网站怎么建立
转载请注明出处:点我 由于Docker自身的网络的原因,想要在多主机间的容器之间进行通信是比较麻烦的事情。可以利用Ambassador容器来实现这一功能。 基本原理: 利用Ambassador来实现主机间容器进行通信时,需要在两台需要通信的容器的…...
wordpress 搬迁插件/网络推广文案有哪些
前面一篇文章讲解了关于java实现excel、word、txt、ppt等办公文件在线预览功能,上文也提到了不支持07版本之后的docx/xslx/pptx。 异常问题 SocketOpenOfficeConnection : connected java.lang.IllegalArgumentException: inputFormat is null at com.artofsolvi…...