JOIN
一、使用方式。
join是Thread类的一个方法,启动线程后直接调用,例如:
1 | Thread t = new AThread(); t.start(); t.join(); |
二、为什么要用join()方法
在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。
三、join方法的作用
在JDk的API里对于join()方法是:
join
public final void join() throws InterruptedException Waits for this thread to die. Throws: InterruptedException - if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.
即join()的作用是:“等待该线程终止”,这里需要理解的就是该线程是指的主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。
四、用实例来理解
写一个简单的例子来看一下join()的用法:
1.AThread 类
-
BThread类
-
TestDemo 类
01classBThreadextendsThread {02publicBThread() {03super("[BThread] Thread");04};05publicvoidrun() {06String threadName = Thread.currentThread().getName();07System.out.println(threadName +" start.");08try{09for(inti =0; i <5; i++) {10System.out.println(threadName +" loop at "+ i);11Thread.sleep(1000);12}13System.out.println(threadName +" end.");14}catch(Exception e) {15System.out.println("Exception from "+ threadName +".run");16}17}18}19classAThreadextendsThread {20BThread bt;21publicAThread(BThread bt) {22super("[AThread] Thread");23this.bt = bt;24}25publicvoidrun() {26String threadName = Thread.currentThread().getName();27System.out.println(threadName +" start.");28try{29bt.join();30System.out.println(threadName +" end.");31}catch(Exception e) {32System.out.println("Exception from "+ threadName +".run");33}34}35}36publicclassTestDemo {37publicstaticvoidmain(String[] args) {38String threadName = Thread.currentThread().getName();39System.out.println(threadName +" start.");40BThread bt =newBThread();41AThread at =newAThread(bt);42try{43bt.start();44Thread.sleep(2000);45at.start();46at.join();47}catch(Exception e) {48System.out.println("Exception from main");49}50System.out.println(threadName +" end!");51}52}打印结果:
修改一下代码:01main start. //主线程起动,因为调用了at.join(),要等到at结束了,此线程才能向下执行。02[BThread] Thread start.03[BThread] Thread loop at 004[BThread] Thread loop at 105[AThread] Thread start. //线程at启动,因为调用bt.join(),等到bt结束了才向下执行。06[BThread] Thread loop at 207[BThread] Thread loop at 308[BThread] Thread loop at 409[BThread] Thread end.10[AThread] Thread end. // 线程AThread在bt.join();阻塞处起动,向下继续执行的结果11main end! //线程AThread结束,此线程在at.join();阻塞处起动,向下继续执行的结果。01publicclassTestDemo {02publicstaticvoidmain(String[] args) {03String threadName = Thread.currentThread().getName();04System.out.println(threadName +" start.");05BThread bt =newBThread();06AThread at =newAThread(bt);07try{08bt.start();09Thread.sleep(2000);10at.start();11//at.join(); //在此处注释掉对join()的调用12}catch(Exception e) {13System.out.println("Exception from main");14}15System.out.println(threadName +" end!");16}17}打印结果:
01main start. // 主线程起动,因为Thread.sleep(2000),主线程没有马上结束;0203[BThread] Thread start. //线程BThread起动04[BThread] Thread loop at 005[BThread] Thread loop at 106main end! // 在sleep两秒后主线程结束,AThread执行的bt.join();并不会影响到主线程。07[AThread] Thread start. //线程at起动,因为调用了bt.join(),等到bt结束了,此线程才向下执行。08[BThread] Thread loop at 209[BThread] Thread loop at 310[BThread] Thread loop at 411[BThread] Thread end. //线程BThread结束了12[AThread] Thread end. // 线程AThread在bt.join();阻塞处起动,向下继续执行的结果五、从源码看join()方法
在AThread的run方法里,执行了bt.join();,进入看一下它的JDK源码:
然后进入join(0L)方法:1publicfinalvoidjoin()throwsInterruptedException {2join(0L);3}01publicfinalsynchronizedvoidjoin(longl)02throwsInterruptedException03{04longl1 = System.currentTimeMillis();05longl2 = 0L;06if(l < 0L)07thrownewIllegalArgumentException("timeout value is negative");08if(l == 0L)09for(; isAlive(); wait(0L));10else11do12{13if(!isAlive())14break;15longl3 = l - l2;16if(l3 <= 0L)17break;18wait(l3);19l2 = System.currentTimeMillis() - l1;20}while(true);21}单纯从代码上看: * 如果线程被生成了,但还未被起动,isAlive()将返回false,调用它的join()方法是没有作用的。将直接继续向下执行。 * 在AThread类中的run方法中,bt.join()是判断bt的active状态,如果bt的isActive()方法返回false,在bt.join(),这一点就不用阻塞了,可以继续向下进行了。从源码里看,wait方法中有参数,也就是不用唤醒谁,只是不再执行wait,向下继续执行而已。 * 在join()方法中,对于isAlive()和wait()方法的作用对象是个比较让人困惑的问题:
isAlive()方法的签名是:public final native boolean isAlive(),也就是说isAlive()是判断当前线程的状态,也就是bt的状态。
wait()方法在jdk文档中的解释如下:
Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0).
The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.
在这里,当前线程指的是at。此处就是为什么阻塞的是at,让bt继续执行下去,直到bt执行完成后,再执行at。转载http://www.open-open.com/lib/view/open1371741636171.html
WAIT/NOTIFY/NITIFYALL
在多线程的情况下,由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。
wait():
等待对象的同步锁,需要获得该对象的同步锁才可以调用这个方法,否则编译可以通过,但运行时会收到一个异常:IllegalMonitorStateExcep
调用任意对象的 wait() 方法导致该线程阻塞,该线程不可继续执行,并且该对象上的锁被释放。
notify():
唤醒在等待该对象同步锁的线程(只唤醒一个,如果有多个在等待),注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
调用任意对象的notify()方法则导致因调用该对象的 wait()方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。
notifyAll():
唤醒所有等待的线程,注意唤醒的是notify之前wait的线程,对于notify之后的wait线程是没有效果的。
通常,多线程之间需要协调工作:如果条件不满足,则等待;当条件满足时,等待该条件的线程将被唤醒。在Java中,这个机制的实现依赖于wait/notify。等待机制与锁机制是密切关联的。
例如:
synchronized(obj) {
while(!condition) {
obj.wait();
}
obj.doSomething();
}
当线程A获得了obj锁后,发现条件condition不满足,无法继续下一处理,于是线程A就wait()。
在另一线程B中,如果B更改了某些条件,使得线程A的condition条件满足了,就可以唤醒线程A :
synchronized(obj) {
condition = true;
obj.notify();
}
需要注意的概念是:
# 调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj){...} 代码段内。
# 调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁,也就无法在synchronized(obj){...} 代码段内唤醒A。
# 当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。
#如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM决定)。
#obj.notifyAll()则能全部唤醒A1,A2,A3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,A1,A2,A3只有一个有机会获得锁继续执行,例如A1,其余的需要等待A1释放obj锁之后才能继续执行。
# 当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此,A1,A2,A3虽被唤醒,但是仍无法获得obj锁。直到B退出synchronized块,释放obj锁后,A1,A2,A3中的一个才有机会获得锁继续执行。
谈一下synchronized和wait()、notify()等的关系:
1.有synchronized的地方不一定有wait,notify
2.有wait,notify的地方必有synchronized.这是因为wait和notify不是属于线程类,而是每一个对象都具有的方法,而且,这两个方法都和对象锁有关,有锁的地方,必有synchronized。
另外,注意一点:如果要把notify和wait方法放在一起用的话,必须先调用notify后调用wait,因为如果调用完wait,该线程就已经不是currentthread了。转载:http://blog.youkuaiyun.com/oracle_microsoft/article/details/6863662
本文详细介绍了Java中线程的join方法的使用场景、原理及实例代码,通过源码解析帮助理解线程间如何协作。
566

被折叠的 条评论
为什么被折叠?



