尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

多线程(一) - Java线程(三) - join方法详解,join阻塞的到底是什么线程

多线程(一) - 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
返回列表