浅析 Java Thread.join()

来源:互联网 发布:数据泵导入指定表空间 编辑:程序博客网 时间:2024/05/01 02:04

引自:http://blog.csdn.net/bzwm/archive/2009/02/12/3881392.aspx

浅析 Java Thread.join() 收藏
一、在研究join的用法之前,先明确两件事情。

1.join方法定义在Thread类中,则调用者必须是一个线程,

例如:

Thread t = new CustomThread();//这里一般是自定义的线程类

t.start();//线程起动

t.join();//此处会抛出InterruptedException异常

 

2.上面的两行代码也是在一个线程里面执行的。

 

以上出现了两个线程,一个是我们自定义的线程类,我们实现了run方法,做一些我们需要的工作;另外一个线程,生成我们自定义线程类的对象,然后执行

customThread.start();

customThread.join();

在这种情况下,两个线程的关系是一个线程由另外一个线程生成并起动,所以我们暂且认为第一个线程叫做“子线程”,另外一个线程叫做“主线程”。

 

二、为什么要用join()方法

主线程生成并起动了子线程,而子线程里要进行大量的耗时的运算(这里可以借鉴下线程的作用),当主线程处理完其他的事务后,需要用到子线程的处理结果,这个时候就要用到join();方法了。

 

 

三、join方法的作用

在网上看到有人说“将两个线程合并”。这样解释我觉得理解起来还更麻烦。不如就借鉴下API里的说法:

“等待该线程终止。”

解释一下,是主线程(我在“一”里已经命名过了)等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。(Waits for this thread to die.)

 

 

四、用实例来理解

写一个简单的例子来看一下join()的用法,一共三个类:

1.CustomThread 类

2. CustomThread1类

3. JoinTestDemo 类,main方法所在的类。

 

代码1:

view plaincopy to clipboardprint?
package wxhx.csdn2;  
/** 
 *  
 * @author bzwm 
 * 
 */ 
class CustomThread1 extends Thread {  
    public CustomThread1() {  
        super("[CustomThread1] Thread");  
    };  
    public void run() {  
        String threadName = Thread.currentThread().getName();  
        System.out.println(threadName + " start.");  
        try {  
            for (int i = 0; i < 5; i++) {  
                System.out.println(threadName + " loop at " + i);  
                Thread.sleep(1000);  
            }  
            System.out.println(threadName + " end.");  
        } catch (Exception e) {  
            System.out.println("Exception from " + threadName + ".run");  
        }  
    }  
}  
class CustomThread extends Thread {  
    CustomThread1 t1;  
    public CustomThread(CustomThread1 t1) {  
        super("[CustomThread] Thread");  
        this.t1 = t1;  
    }  
    public void run() {  
        String threadName = Thread.currentThread().getName();  
        System.out.println(threadName + " start.");  
        try {  
            t1.join();  
            System.out.println(threadName + " end.");  
        } catch (Exception e) {  
            System.out.println("Exception from " + threadName + ".run");  
        }  
    }  
}  
public class JoinTestDemo {  
    public static void main(String[] args) {  
        String threadName = Thread.currentThread().getName();  
        System.out.println(threadName + " start.");  
        CustomThread1 t1 = new CustomThread1();  
        CustomThread t = new CustomThread(t1);  
        try {  
            t1.start();  
            Thread.sleep(2000);  
            t.start();  
            t.join();//在代碼2里,將此處注釋掉  
        } catch (Exception e) {  
            System.out.println("Exception from main");  
        }  
        System.out.println(threadName + " end!");  
    }  

package wxhx.csdn2;
/**
 *
 * @author bzwm
 *
 */
class CustomThread1 extends Thread {
    public CustomThread1() {
        super("[CustomThread1] Thread");
    };
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " start.");
        try {
            for (int i = 0; i < 5; i++) {
                System.out.println(threadName + " loop at " + i);
                Thread.sleep(1000);
            }
            System.out.println(threadName + " end.");
        } catch (Exception e) {
            System.out.println("Exception from " + threadName + ".run");
        }
    }
}
class CustomThread extends Thread {
    CustomThread1 t1;
    public CustomThread(CustomThread1 t1) {
        super("[CustomThread] Thread");
        this.t1 = t1;
    }
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " start.");
        try {
            t1.join();
            System.out.println(threadName + " end.");
        } catch (Exception e) {
            System.out.println("Exception from " + threadName + ".run");
        }
    }
}
public class JoinTestDemo {
    public static void main(String[] args) {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " start.");
        CustomThread1 t1 = new CustomThread1();
        CustomThread t = new CustomThread(t1);
        try {
            t1.start();
            Thread.sleep(2000);
            t.start();
            t.join();//在代碼2里,將此處注釋掉
        } catch (Exception e) {
            System.out.println("Exception from main");
        }
        System.out.println(threadName + " end!");
    }
}

 

打印结果:

 

main start.//main方法所在的线程起动,但没有马上结束,因为调用t.join();,所以要等到t结束了,此线程才能向下执行。

[CustomThread1] Thread start.//线程CustomThread1起动

[CustomThread1] Thread loop at 0//线程CustomThread1执行

[CustomThread1] Thread loop at 1//线程CustomThread1执行

[CustomThread] Thread start.//线程CustomThread起动,但没有马上结束,因为调用t1.join();,所以要等到t1结束了,此线程才能向下执行。

[CustomThread1] Thread loop at 2//线程CustomThread1继续执行

[CustomThread1] Thread loop at 3//线程CustomThread1继续执行

[CustomThread1] Thread loop at 4//线程CustomThread1继续执行

[CustomThread1] Thread end. //线程CustomThread1结束了

[CustomThread] Thread end.// 线程CustomThread在t1.join();阻塞处起动,向下继续执行的结果

main end!//线程CustomThread结束,此线程在t.join();阻塞处起动,向下继续执行的结果。

 

修改一下代码,得到代码2:(这里只写出修改的部分)

view plaincopy to clipboardprint?
public class JoinTestDemo {  
    public static void main(String[] args) {  
        String threadName = Thread.currentThread().getName();  
        System.out.println(threadName + " start.");  
        CustomThread1 t1 = new CustomThread1();  
        CustomThread t = new CustomThread(t1);  
        try {  
            t1.start();  
            Thread.sleep(2000);  
            t.start();  
//          t.join();//在代碼2里,將此處注釋掉  
        } catch (Exception e) {  
            System.out.println("Exception from main");  
        }  
        System.out.println(threadName + " end!");  
    }  

public class JoinTestDemo {
    public static void main(String[] args) {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " start.");
        CustomThread1 t1 = new CustomThread1();
        CustomThread t = new CustomThread(t1);
        try {
            t1.start();
            Thread.sleep(2000);
            t.start();
//            t.join();//在代碼2里,將此處注釋掉
        } catch (Exception e) {
            System.out.println("Exception from main");
        }
        System.out.println(threadName + " end!");
    }
}

 

打印结果:

 

main start. // main方法所在的线程起动,但没有马上结束,这里并不是因为join方法,而是因为Thread.sleep(2000);

[CustomThread1] Thread start. //线程CustomThread1起动

[CustomThread1] Thread loop at 0//线程CustomThread1执行

[CustomThread1] Thread loop at 1//线程CustomThread1执行

main end!// Thread.sleep(2000);结束,虽然在线程CustomThread执行了t1.join();,但这并不会影响到其他线程(这里main方法所在的线程)。

[CustomThread] Thread start. //线程CustomThread起动,但没有马上结束,因为调用t1.join();,所以要等到t1结束了,此线程才能向下执行。

[CustomThread1] Thread loop at 2//线程CustomThread1继续执行

[CustomThread1] Thread loop at 3//线程CustomThread1继续执行

[CustomThread1] Thread loop at 4//线程CustomThread1继续执行

[CustomThread1] Thread end. //线程CustomThread1结束了

[CustomThread] Thread end. // 线程CustomThread在t1.join();阻塞处起动,向下继续执行的结果

 

 

五、从源码看join()方法

 

在CustomThread的run方法里,执行了t1.join();,进入看一下它的JDK源码:

view plaincopy to clipboardprint?
public final void join() throws InterruptedException {  
n(0);  

    public final void join() throws InterruptedException {
    join(0);
    }
 

然后进入join(0)方法:

 

view plaincopy to clipboardprint?
   /** 
    * Waits at most <code>millis</code> milliseconds for this thread to  
    * die. A timeout of <code>0</code> means to wait forever. //注意这句 
    * 
    * @param      millis   the time to wait in milliseconds. 
    * @exception  InterruptedException if another thread has interrupted 
    *             the current thread.  The <i>interrupted status</i> of the 
    *             current thread is cleared when this exception is thrown. 
    */ 
   public final synchronized void join(long millis) //参数millis为0.  
   throws InterruptedException {  
long base = System.currentTimeMillis();  
long now = 0;  
if (millis < 0) {  
           throw new IllegalArgumentException("timeout value is negative");  
}  
if (millis == 0) {//进入这个分支  
    while (isAlive()) {//判断本线程是否为活动的。这里的本线程就是t1.  
    wait(0);//阻塞  
    }  
} else {  
    while (isAlive()) {  
    long delay = millis - now;  
    if (delay <= 0) {  
        break;  
    }  
    wait(delay);  
    now = System.currentTimeMillis() - base;  
    }  
}  
   } 
    /**
     * Waits at most <code>millis</code> milliseconds for this thread to
     * die. A timeout of <code>0</code> means to wait forever. //注意这句
     *
     * @param      millis   the time to wait in milliseconds.
     * @exception  InterruptedException if another thread has interrupted
     *             the current thread.  The <i>interrupted status</i> of the
     *             current thread is cleared when this exception is thrown.
     */
    public final synchronized void join(long millis) //参数millis为0.
    throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;
    if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
    }
    if (millis == 0) {//进入这个分支
        while (isAlive()) {//判断本线程是否为活动的。这里的本线程就是t1.
        wait(0);//阻塞
        }
    } else {
        while (isAlive()) {
        long delay = millis - now;
        if (delay <= 0) {
            break;
        }
        wait(delay);
        now = System.currentTimeMillis() - base;
        }
    }
    }
 

 

 

 

单纯从代码上看,如果线程被生成了,但还未被起动,调用它的join()方法是没有作用的。将直接继续向下执行,这里就不写代码验证了。

发表于 @ 2009年02月12日 13:34:00 | 评论( 4 ) | 编辑| 举报| 收藏

旧一篇:java实现数据库连接池 | 新一篇:try-catch-finally的用法之finallygrant999 发表于2009年2月15日 18:14:05  IP:举报
if (millis == 0) {//进入这个分支
while (isAlive()) {//判断本线程是否为活动的。这里的本线程就是t1.
wait(0);//阻塞
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
grant999 发表于2009年2月15日 18:14:50  IP:举报
if (millis == 0) {//进入这个分支
while (isAlive())
wait(0);//进入阻塞状态,是如何唤醒的呢?????
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
bzwm 发表于2009年2月16日 9:48:22  IP:举报
Re grant999: wait方法是有参数的。
isAlive()返回false的话,则不去阻塞,所以可以继续向下执行。
另,可参见上一个回复。grant999 发表于2009年2月15日 18:26:53  IP:举报
是不是线程死亡的时候,即not active,会唤醒等待队列里的线程,在本例中t1死亡前会通知自己wait set里的线程???bzwm 发表于2009年2月16日 9:47:04  IP:举报
Re grant999: 在CustomThread类中的run方法中,t1.join(),是判断t1的active状态,如果t1的isActive()方法返回false,根据源码,在t1.join(),这一点就不用阻塞了,可以继续向下进行了。
从源码里看,wait方法中有参数,也就是不用唤醒谁,只是不再执行wait,向下继续执行而已。grant999 发表于2009年2月19日 22:38:26  IP:举报
下面是jdk帮助文档关于wait(0)的说明:
The specified amount of real time has elapsed,more or less.If timeout is zero,however,the real time is not taken into consideration and the thread simply waits until notified.

说明当参数为0时,线程还是要无限等待下去,直到别人来唤醒它,如果参数大于0,时间到了它自己会醒的
bzwm 发表于2009年2月20日 15:45:30  IP:举报
Re grant999: 这个jdk源码里的注释我看过了。
你有没有考虑过,如果wait()方法里的参数是个随便的值,暂且认为是 wait(1000) 好了。
那为何线程会阻塞1秒后就自动醒来了呢?也没有人执行notify方法啊。

另外,你在论坛发的帖子我已经回了。
要想弄明白到底是怎样被唤醒的,就要研究更底层的东西了。

毕竟这些东西是借助native的方法实现的。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/bzwm/archive/2009/02/12/3881392.aspx

原创粉丝点击