JOIN的线程谁来唤醒

来源:互联网 发布:python 查看变量大小 编辑:程序博客网 时间:2024/05/22 17:01

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偶尔也能正确工作(只有一个等待条件下)。

0 0