JOIN的线程谁来唤醒
这问题简单回答就是: 主线程a 调用来子线程b的join方法后后,a将进入b对象的锁池队列里等待b的锁,同时有可能别的线程会调用b线程的notifyALL方法,无意中唤醒a线程,
而此时b线程还有终止,所以要在while中判断b.isAlive().
Java里的线程是映射到本地OS线程的,所以线程结束其实是OS线程结束,而此时JVM里的代表线程的包装类Java.lang.Thread对象其实还在的,只是b.isAlive()会返回false了.
等待b线程对象的Java.lang.Thread对象没有人引用的时候,它会被GC回收掉.
转自http://blog.csdn.net/axman/article/details/3986918
JOIN的线程谁来唤醒
这个问题在CSDN上被问到,我做了详细的回答。但提问者的水平不同,对正确答案的判断能力也不同,所他并没有满意我的回答。
我之所以专门再为此写一篇文章,是从这个问题要引出话题还是应该要阐明的。
1.join做了什么?
 打开jdk的源码,可以看到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的时间,就在目标线程的wait指定
的时间内wait,如果没有指定时间,在线程的存活期内一直wait.
除了wait(time)可以到时被线程调度唤醒(有人说这叫自己醒来),否则当前线程一直在
while (isAlive()) { wait(0); }
    这段代码是在目标线程中实现的供当前调用目标线程.join()的线程执行的,所以isAlive()当然是指目标线程
即,当前线程a调用线程b.join();那么isAlive()是线程b的。从线程b中看是this.isAlive(),如果从线程a中看就是
b.isAlive().同样wait是当前线程在目标线程对象上等待。那么当前线程等待什么?
 当然是等待被notify(All)唤醒。没错,但这只是一个表面的现象,我要说的是等待条件。
 等待条件决定是谁在唤醒调用join的线程(我上面一直说的当前线程,其实调用后它就不是当前线程了)
 这里因为等待条件是b.isAlive();所以当线程b is Not Alive时,一定会唤醒在它上面调用join的方法的线程a.
我们可以看到线程退出时的实现:
static void ensure_join(JavaThread* thread) { Handle threadObj(thread, thread->threadObj()); assert(threadObj.not_null(), "java thread object must exist"); ObjectLocker lock(threadObj, thread); thread->clear_pending_exception(); java_lang_Thread::set_stillborn(threadObj()); java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED); java_lang_Thread::set_thread(threadObj(), NULL); lock.notify_all(thread); thread->clear_pending_exception(); }
这个本地方法在线程退出时被调用,它除了做其它收尾工作,一定会唤醒在这个线程对象上wait的所有线程。因为是本地方法,
我们在JDK SRC中看不到被调用join的线程唤醒调用join的线程。但是
wait方法除了wait到期被线程调度唤醒外,始终等待wait的反条件。
同时,也把唤醒责任交给了改变那个条件的线程,所以如果要看一个wait中的线程被谁唤醒,就要看谁在控制这个条件,如果控制这个条件的线程始终没有唤醒调用wait的线程,那么就是一个到致命的设计错误。
这里,我们知道join其实最终是wait,是a调用b.wait(),假如:
while(b.isAlive()){
   wait();//其实就是this.wait();a.wait();
}
行
不行?完全可以,只是b在退出时要知道谁在等待b is not 
Alive这个条件,然后通知这个线程醒来,因为b在自己的实例上wait(),所以b要调用a.notify/All()才能唤醒,而如果在一开始没有
一个地方标记了a在等待这个条件,b也就无法知道a在等待这个条件,所以a才在b的对象上wait,但如果我们自己控制编程,有一个全局变量或数据结果保
存等待这个条件的线程句柄,那么完全可以在控制这个条件的线程中去唤醒,或者a可以在第三方不相关的对象上wait,然后控制这个条件的线程在达到非
wait条件时在这个第三方对象上调用notify/All。
所以真正唤醒join/wait中的线程的责任者是能够使等待条件不成立的线程。而wait中的线程真正等待的是反wait条件。
假如有一个List 对象 aList
while(aList.size() <= 0){
   wait();
}
aList.removeFisrt();
我们知道现在这个线和要得到list的第一个元素,但如果list中没有元素就要wait.那么谁来唤醒它?当然是往list中加元素的
线程,所以如果有一个线程
aList.add(o);一定要调用notify/All();否则就是设计错误。至于调用谁的notify/All()就要看等待的线程在哪个对象上等待。
再次说明while(条件)的作用:
这是一个多线程版的安全的if(条件)。
在多线程环境下,如果只用if():
 if(aList.size() <=0) aList.wait(); //假如就在list对象上wait,这也是一个非常好选择,因为add和remove都操作
        //共同的对象是list,它也就成了共同可知的标记对象。
 aList.removeFisrt();
线程a和线程b都执行到这里了。都在等待aList.size() > 0的条件。现在线程c往其中加了一个元素:
aList.add(o);
aList.notifyAll();
OK,线程a,b都被唤醒,a执行wait()的后的代码,正确地从aList中remove了一个对象。然后切换到b运行,b也从wait后的代码
运行,结果aList没有元素了,b说,KAO,上当了,抛出异常吧。wait条件判断失败。
如果是while(aList.size() <=0) aList.wait();那么线程a从wait()后执行,又进循环,一看aList.size() >0了,所以直接执行
removeFirst,然后b也从wait后执行,又回到条件判断,一看aList.size() <= 0了,哦,只是我慢了一步,那我再等吧。
判断条件正确地得到了控制。
另外,处在wait中的线程不一定都是由控制循环条件的线程唤醒,比如你现在要等人,那个人四点钟来,可是现在才两点,现在要睡觉,你叫你的同伴在四点的时候叫醒你。如果用
if(当前时间 < 四点)
我.wait();
meet a person;
在四点之前,不一定只有你的同伴会叫醒你,有可能在3点的时候一个酒鬼会过来吵醒你,这时你醒来了,执行下面meet a person;但因为时间条件还没有到,所以那个person为null,但如果是:
while(当前时间 < 四点)
我.wait();
meet a person;
不管在中点之前有多少人叫醒你,你都会看一下时间是否到四点,只有到了四点你才会meet a person.
所以应该成为习惯,应该坚持用while(条件)来wait而不是if.尽管 if偶尔也能正确工作(只有一个等待条件下)。
 
                    
                     
                    
                 
                    
                 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号