尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
多线程(一) - Java线程(三) - join方法详解,join阻塞的到底是什么线程
理解join()方法之前请确保对wait()/notify()/notifyAll()机制已熟练掌握。1. join()方法的作用是等待线程销毁。join()方法反应的是一个很现实的问题比如main线程的执行时间是1s子线程的执行时间是10s但是主线程依赖子线程执行完的结果这时怎么办可以像生产者/消费者模型一样搞一个缓冲区子线程执行完把数据放在缓冲区中通知main线程main线程去拿这样就不会浪费main线程的时间了。另外一种方法就是join()了。2. 例一阻塞mian线程先来看一段代码staticintr0;publicstaticvoidmain(String[]args)throwsInterruptedException{test1();}privatestaticvoidtest1()throwsInterruptedException{log.debug(开始);Threadt1newThread(()-{log.debug(开始);try{Thread.sleep(100);}catch(InterruptedExceptione){e.printStackTrace();}log.debug(结束);r10;},t1);t1.start();// t1.join();log.debug(结果为{},r);}运行结果可以看到在r的值并没有被修改就被打印了。10:44:40.264[main]DEBUGjoinTest-开始10:44:40.273[t1]DEBUGjoinTest-开始10:44:40.272[main]DEBUGjoinTest-结果为010:44:40.375[t1]DEBUGjoinTest-结束我们取消对t1.join()的注释其余不变查看结果10:49:00.045[main]DEBUGjoinTest-开始10:49:00.053[t1]DEBUGjoinTest-开始10:49:00.162[t1]DEBUGjoinTest-结束10:49:00.163[main]DEBUGjoinTest-结果为10意思是join()方法会使调用join()方法的线程也就是t1线程所在的线程也就是main线程无限阻塞直到调用join()方法的线程销毁为止此例中main线程就会无限期阻塞直到t1的run()方法执行完毕。3. 例二不能阻塞同级线程如果大家对于上述的例子还有疑问比如有没有可能是2个并行线程执行join方法阻塞另一个看一个例子publicclassTestJoinimplementsRunnable{Overridepublicvoidrun(){for(inti0;i5;i){System.out.println(vip来了iThread.currentThread().getName());try{Thread.sleep(100);}catch(InterruptedExceptione){e.printStackTrace();}}}publicstaticvoidmain(String[]args)throwsInterruptedException{TestJointestJoinnewTestJoin();ThreadanewThread(testJoin,a);ThreadbnewThread(testJoin,b);a.start();b.start();a.join();System.out.println(我是main我完了);}}结果vip来了0b vip来了0a vip来了1b vip来了1a vip来了2b vip来了2a vip来了3b vip来了3a vip来了4a vip来了4b 我是main我完了如果按上述想法很明显应该等a执行完再执行b很明显结果并不是这样结果是调用join方法的main线程在等a线程和b线程执行完成后再执行。在看一个例子证明是调用方也就是父线程被阻塞4. 例三阻塞调用方线程publicclassTestJoin2{publicstaticvoidmain(String[]args){StringthreadNameThread.currentThread().getName();System.out.println(threadName start.);BThreadbtnewBThread();AThreadatnewAThread(bt);try{bt.start();Thread.sleep(2000);at.start();at.join();}catch(Exceptione){System.out.println(Exception from main);}System.out.println(threadName end!);}}classBThreadextendsThread{publicBThread(){super([BThread] Thread);};publicvoidrun(){StringthreadNameThread.currentThread().getName();System.out.println(threadName start.);try{for(inti0;i5;i){System.out.println(threadName loop at i);Thread.sleep(1000);}System.out.println(threadName end.);}catch(Exceptione){System.out.println(Exception from threadName.run);}}}classAThreadextendsThread{BThreadbt;publicAThread(BThreadbt){super([AThread] Thread);this.btbt;}publicvoidrun(){StringthreadNameThread.currentThread().getName();System.out.println(threadName start.);try{bt.join();System.out.println(threadName end.);}catch(Exceptione){System.out.println(Exception from threadName.run);}}}打印结果main start.[BThread]Threadstart.// 父线程B启动执行[BThread]Threadloop at0[BThread]Threadloop at1[AThread]Threadstart.// 2秒后main线程睡眠结束启动at线程at线程被bt.join阻塞[BThread]Threadloop at2[BThread]Threadloop at3[BThread]Threadloop at4[BThread]Threadend.[AThread]Threadend.// bt线程执行完at线程开始执行main end!// 由于调用了at.join所以只能等待at线程执行完再执行at的父线程main线程做一些修改将上述的main方法中的at.join()注释查看运行结果main start.[BThread]Threadstart.// bt线程启动[BThread]Threadloop at0[BThread]Threadloop at1main end!// main线程由于没有被阻塞所以和其他线程并行执行。此时main线程已经执行结束[AThread]Threadstart.// at线程启动由于被bt.join阻塞只能等bt线程执行完执行[BThread]Threadloop at2[BThread]Threadloop at3[BThread]Threadloop at4[BThread]Threadend.[AThread]Threadend.// at线程执行从这个例子可以看出来阻塞的是调用join方法的线程的调用线程。5. 例四带参数来完成限时同步join是可以带参数的join(2000)也是可以的表示调用join()方法的线程所在的线程最多等待2000ms也就是比如main线程只会被阻塞2000毫秒。staticintr10;staticintr20;publicstaticvoidmain(String[]args){test();}publicstaticvoidtest(){try{Threadt1newThread(()-{try{Thread.sleep(2000);r110;}catch(InterruptedExceptione){e.printStackTrace();}});LongstartSystem.currentTimeMillis();t1.start();log.debug(join begin);// 表示主线程只会等待1500毫秒t1.join(1500);LongendSystem.currentTimeMillis();log.debug(r1:{} r2: {} cost:{},r1,r2,end-start);}catch(InterruptedExceptione){e.printStackTrace();}}结果21:44:01.942[main]DEBUG com.multiThread.TestJoin -joinbegin# 可以看到r1的结果还是0因为在1500毫秒的时候主线程就已经执行了输出语句了21:44:03.445[main]DEBUG com.multiThread.TestJoin - r1:0 r2:0cost:1506如果将上面的join时间改为3000毫秒来看看执行结果21:47:50.881[main]DEBUG com.multiThread.TestJoin -joinbegin# 可以看到r1的结果是10被阻塞到调用join方法的线程执行完成后立即执行# 并不会等到3秒后再执行21:47:52.879[main]DEBUG com.multiThread.TestJoin - r1:10 r2:0cost:20006. 例五主线程等待多个线程的运行结果staticintr10;staticintr20;privatestaticvoidtest2()throwsInterruptedException{log.debug(开始);Threadt1newThread(()-{try{Thread.sleep(100);}catch(InterruptedExceptione){e.printStackTrace();}r110;},t1);Threadt2newThread(()-{try{Thread.sleep(200);}catch(InterruptedExceptione){e.printStackTrace();}r220;},t1);t1.start();t2.start();longstartSystem.currentTimeMillis();log.debug(join begin);t1.join();log.debug(t1 join end);t2.join();log.debug(t2 join end);longendSystem.currentTimeMillis();log.debug(r1:{} r2:{} cost:{},r1,r2,end-start);}结果11:45:28.365[main]DEBUGjoinTest-开始11:45:28.373[main]DEBUGjoinTest-join begin11:45:28.474[main]DEBUGjoinTest-t1 join end11:45:28.579[main]DEBUGjoinTest-t2 join end11:45:28.579[main]DEBUGjoinTest-r110 r2:20cost:206如果吧t1.join和t2.join的顺序调换一下结果11:54:52.242[main]DEBUGjoinTest-开始11:54:52.247[main]DEBUGjoinTest-join begin11:54:52.450[main]DEBUGjoinTest-t1 join end11:54:52.450[main]DEBUGjoinTest-t2 join end11:54:52.450[main]DEBUGjoinTest-r1:10r2:20cost:203原理示例图7. 和sleep的区别join()方法的一个重点是要区分出和sleep()方法的区别。。两者的区别在于sleep(2000)不释放锁join(2000)释放锁因为join()方法内部使用的是wait()因此会释放锁。看一下join(2000)的源码就知道了join()其实和join(2000)一样无非是join(0)而已publicfinalsynchronizedvoidjoin(finallongmillis)throwsInterruptedException{if(millis0){if(isAlive()){finallongstartTimeSystem.nanoTime();longdelaymillis;do{wait(delay);}while(isAlive()(delaymillis-TimeUnit.NANOSECONDS.toMillis(System.nanoTime()-startTime))0);}}elseif(millis0){while(isAlive()){wait(0);}}else{thrownewIllegalArgumentException(timeout value is negative);}}8. join与异常如果join过程中当前线程对象被中断则出现线程出现异常参考https://blog.csdn.net/sinat_29384657/article/details/52228578https://www.cnblogs.com/xrq730/p/4851233.html
RELATED

相关推荐

多线程(一) - java线程(四) - interrupt方法详解

多线程(一) - java线程(四) - interrupt方法详解

1. interrupt打断线程 1.1 打断sleep,wait,join(阻塞状态)的线程 public static void main(String[] args) throws InterruptedException{Thread t1 new Thread(() -> {log.debug("sleep;;;");try {Thread.sleep(50…

📅 2026/10/25 6:07:01
337. 打家劫舍 III(dfs)

337. 打家劫舍 III(dfs)

链接:337. 打家劫舍 III - 力扣(LeetCode) 题解: 代码: 1.打劫当前节点:root->val left和right不打劫的最大值 2.不打劫当前节点:left打劫和不打劫的最大值right打劫和不打劫的最大值 /…

📅 2026/10/25 6:07:18
基于SpringBoot的米村拌饭订餐管理系统的设计与实现(源码+lw+部署文档+讲解等)

基于SpringBoot的米村拌饭订餐管理系统的设计与实现(源码+lw+部署文档+讲解等)

联系博主 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 …

📅 2026/9/8 2:22:40
MORE NEWS

更多资讯

📰

RRF 融合后还要不要重排:先画候选召回上限

RRF 能把不同检索通道的排名合到一起,但它不能把没召回的文档变出来。重排器也是一样。很多“换了重排模型还是答错”的问题,实际发生在候选池形成之前。 10 月 8 日,Qdrant 与 Pento 发布了 miniCOIL EN-ES 的双语稀疏检索研究预览&#xf…

📰

199_负载突变下转速环积分饱和的恢复过程设计

从一个反复烧驱动模块的现场说起 几年前做过一台大惯量旋转台的项目,电机通过减速机构拖动一个偏心负载。空载调试一切正常,转速环响应干脆利落。但是一旦把真实负载挂上去,做突加突卸测试,问题就来了:每次负载突然卸掉的瞬间,转速会猛地冲上去,超调大得吓人,紧接着驱…

📰

198_弱磁区电压裕量不足时的电流分配策略调整

一个把母线电压吃干榨净的现场 早年间做一套宽调速范围的永磁同步驱动项目,样机在额定转速以下跑得四平八稳,温升、噪声、效率都在预期内。问题出在往高速推的阶段:转速刚越过基速不久,控制器就开始报母线电压利用率异常,同时相电流波形上出现明显的削顶畸变,转矩输出像…

📰

197_电流环与速度环带宽配比失当引起的振荡现象

前年冬天,有个做卷绕设备的项目找到我。他们的机器在低速运行时一切正常,但产线速度提上去之后,张力就开始周期性波动,幅度越来越大,最后设备自己保护停机。现场工程师换了张力传感器、检查了机械传动、甚至把电机都拆下来重新对中,折腾了快两周,问题依旧。 我到现场之…

📰

196_反电动势谐波对转子位置估算误差的耦合分析

从一次诡异的低速抖动说起 去年冬天调一台无位置传感器永磁同步驱动样机,额定转速三千转,带载运行在中高速区间一切正常。但客户要求必须能稳定运行在百分之五额定转速以下,问题就来了。低速轻载时电机发出周期性的“咯噔咯噔”抖动,电流波形上叠加了明显的六次谐波纹波,…

📰

Agent多Agent框架实战:Router路由器与四种多智能体架构区别说明(TaoToken统一API接入)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬