thread join分析

本文详细阐述了Java线程中的join方法的使用场景、作用原理及通过实例代码展示其具体应用,帮助开发者理解和掌握如何在多线程环境下有效利用join方法。
 

http://blog.163.com/fanning_7213/blog/static/2496505201071255157497/

一、在研究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

  1. package wxhx.csdn2;  
  2. /** 
  3.  *  
  4.  * @author bzwm 
  5.  * 
  6.  */  
  7. class CustomThread1 extends Thread {  
  8.     public CustomThread1() {  
  9.         super("[CustomThread1] Thread");  
  10.     };  
  11.     public void run() {  
  12.         String threadName = Thread.currentThread().getName();  
  13.         System.out.println(threadName + " start.");  
  14.         try {  
  15.             for (int i = 0; i < 5; i++) {  
  16.                 System.out.println(threadName + " loop at " + i);  
  17.                 Thread.sleep(1000);  
  18.             }  
  19.             System.out.println(threadName + " end.");  
  20.         } catch (Exception e) {  
  21.             System.out.println("Exception from " + threadName + ".run");  
  22.         }  
  23.     }  
  24. }  
  25. class CustomThread extends Thread {  
  26.     CustomThread1 t1;  
  27.     public CustomThread(CustomThread1 t1) {  
  28.         super("[CustomThread] Thread");  
  29.         this.t1 = t1;  
  30.     }  
  31.     public void run() {  
  32.         String threadName = Thread.currentThread().getName();  
  33.         System.out.println(threadName + " start.");  
  34.         try {  
  35.             t1.join();  
  36.             System.out.println(threadName + " end.");  
  37.         } catch (Exception e) {  
  38.             System.out.println("Exception from " + threadName + ".run");  
  39.         }  
  40.     }  
  41. }  
  42. public class JoinTestDemo {  
  43.     public static void main(String[] args) {  
  44.         String threadName = Thread.currentThread().getName();  
  45.         System.out.println(threadName + " start.");  
  46.         CustomThread1 t1 = new CustomThread1();  
  47.         CustomThread t = new CustomThread(t1);  
  48.         try {  
  49.             t1.start();  
  50.             Thread.sleep(2000);  
  51.             t.start();  
  52.             t.join();//在代碼2里,將此處注釋掉  
  53.         } catch (Exception e) {  
  54.             System.out.println("Exception from main");  
  55.         }  
  56.         System.out.println(threadName + " end!");  
  57.     }  
  58. }  
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.// 线程CustomThreadt1.join();阻塞处起动,向下继续执行的结果

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

 

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

  1. public class JoinTestDemo {  
  2.     public static void main(String[] args) {  
  3.         String threadName = Thread.currentThread().getName();  
  4.         System.out.println(threadName + " start.");  
  5.         CustomThread1 t1 = new CustomThread1();  
  6.         CustomThread t = new CustomThread(t1);  
  7.         try {  
  8.             t1.start();  
  9.             Thread.sleep(2000);  
  10.             t.start();  
  11. //          t.join();//在代碼2里,將此處注釋掉  
  12.         } catch (Exception e) {  
  13.             System.out.println("Exception from main");  
  14.         }  
  15.         System.out.println(threadName + " end!");  
  16.     }  
  17. }  
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. // 线程CustomThreadt1.join();阻塞处起动,向下继续执行的结果

 

 

五、从源码看join()方法

 

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

  1. public final void join() throws InterruptedException {  
  2. n(0);  
  3. }  
public final void join() throws InterruptedException { join(0); }

然后进入join(0)方法:

 

  1.    /** 
  2.     * Waits at most <code>millis</code> milliseconds for this thread to  
  3.     * die. A timeout of <code>0</code> means to wait forever. //注意这句 
  4.     * 
  5.     * @param      millis   the time to wait in milliseconds. 
  6.     * @exception  InterruptedException if another thread has interrupted 
  7.     *             the current thread.  The <i>interrupted status</i> of the 
  8.     *             current thread is cleared when this exception is thrown. 
  9.     */  
  10.    public final synchronized void join(long millis) //参数millis为0.  
  11.    throws InterruptedException {  
  12. long base = System.currentTimeMillis();  
  13. long now = 0;  
  14. if (millis < 0) {  
  15.            throw new IllegalArgumentException("timeout value is negative");  
  16. }  
  17. if (millis == 0) {//进入这个分支  
  18.     while (isAlive()) {//判断本线程是否为活动的。这里的本线程就是t1.  
  19.     wait(0);//阻塞  
  20.     }  
  21. } else {  
  22.     while (isAlive()) {  
  23.     long delay = millis - now;  
  24.     if (delay <= 0) {  
  25.         break;  
  26.     }  
  27.     wait(delay);  
  28.     now = System.currentTimeMillis() - base;  
  29.     }  
  30. }  
  31.    }  
/** * 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()方法是没有作用的。将直接继续向下执行,这里就不写代码验证了。

<think>我们正在讨论Java中的Thread类和join方法。用户的问题是关于线程(Thread)和join方法的作用及区别。根据提供的引用内容,我们可以总结如下: 1. **线程(Thread)的作用**: - 线程是程序执行的最小单位,允许程序同时执行多个任务。 - 通过创建Thread对象并启动(start()方法),可以执行并行任务。 2. **join方法的作用**: - join方法是Thread类的一个方法,用于等待该线程结束。 - 当在一个线程中调用另一个线程的join方法时,当前线程会被阻塞,直到被调用join的线程执行完毕。 - 这样可以控制线程的执行顺序,确保一个线程在另一个线程完成后才继续执行。 3. **区别**: - Thread类代表一个线程,而join方法是Thread类的一个实例方法,用于线程间同步。 - 使用线程是为了并发执行,而使用join方法是为了等待线程结束,实现顺序控制。 根据引用内容,我们有以下具体解释: - 引用[1]和[2]说明了join方法的作用是让一个线程等待另一个线程完成。 - 引用[3]提供了一个代码示例,展示了join方法如何让主线程等待子线程结束。 - 引用[4]解释了在子线程耗时操作时,主线程需要等待子线程结果的情况下使用join。 因此,我们可以这样回答用户: 线程(Thread)是Java中实现多任务并发执行的基本单元。通过创建线程,我们可以同时执行多个任务,提高程序的效率。 join方法是Thread类的一个方法,用于等待该线程执行结束。当在一个线程(比如主线程)中调用另一个线程(子线程)的join方法时,调用线程会被阻塞,直到子线程运行结束。这样可以确保在需要子线程处理结果的情况下,主线程能够正确获取到结果后再继续执行。 区别总结: - Thread是并发执行的载体,而join是控制线程执行顺序的工具。 - 使用线程是为了并行,使用join是为了同步(等待)。 下面我们通过一个简单的代码示例来说明join方法的使用: ```java public class JoinExample { public static void main(String[] args) { Thread thread = new Thread(() -> { System.out.println("子线程开始执行..."); try { Thread.sleep(2000); // 模拟耗时操作 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("子线程执行完毕"); }); thread.start(); // 启动子线程 try { System.out.println("主线程等待子线程结束"); thread.join(); // 主线程等待子线程结束 System.out.println("主线程继续执行"); } catch (InterruptedException e) { e.printStackTrace(); } } } ``` 运行结果: ``` 主线程等待子线程结束 子线程开始执行... (等待2秒) 子线程执行完毕 主线程继续执行 ``` 如果不使用join方法,主线程可能先于子线程结束,导致无法获取子线程的处理结果。 因此,join方法在多线程编程中用于协调线程的执行顺序,确保依赖关系得到满足。 相关问题: 1. 除了join方法,还有哪些方式可以实现线程间的同步? 2. join方法有哪些重载形式?它们有什么区别? 3. 在多线程编程中,如何避免死锁? 4. 线程的状态有哪些?join方法调用时线程处于什么状态?</think>### Threadjoin 方法的作用及区别 #### 一、Thread(线程)的作用 线程是程序执行的最小单元,主要作用包括: 1. **并发执行**:允许程序同时处理多个任务(如后台计算和UI响应并行) 2. **资源利用**:充分利用多核CPU性能 3. **异步操作**:避免阻塞主线程(如网络请求、文件读写) 4. **任务隔离**:将不同任务隔离到独立执行环境 #### 二、join() 方法的作用 `join()` 是 Thread 类的方法,核心作用是**线程同步**: ```java // 典型用法 Thread worker = new Thread(() -> {...}); worker.start(); worker.join(); // 主线程在此等待worker结束 ``` 1. **阻塞调用者**:调用 `thread.join()` 的线程会阻塞,直到 `thread` 终止 2. **顺序控制**:确保子线程完成后再执行后续代码(如等待数据处理结果) 3. **结果依赖**:当主线程需要子线程的计算结果时(引用[4]) #### 三、关键区别 | 特性 | Thread | join() 方法 | |--------------|--------------------------------|--------------------------------| | **本质** | 并发执行的载体 | 线程同步工具 | | **主要目的** | 实现并行任务 | 控制线程执行顺序 | | **行为** | 通过 start() 启动异步执行 | 阻塞当前线程等待目标线程结束 | | **使用场景** | 任何需要并发的场景 | 线程间存在依赖关系时(引用[2]) | #### 四、典型场景分析 1. **不使用 join()(错误示范)** ```java new Thread(() -> System.out.print("B")).start(); System.out.print("A"); // 可能输出 "A B" 或 "B A" ``` 2. **使用 join() 保证顺序** ```java Thread t = new Thread(() -> System.out.print("B")); t.start(); t.join(); // 主线程等待t结束 System.out.print("A"); // 固定输出 "B A"(引用[3]) ``` #### 五、实现原理 1. `join()` 底层基于 **wait/notify 机制**: ```java public final void join() throws InterruptedException { synchronized(this) { // 获取线程对象锁 while (isAlive()) { wait(0); // 释放锁并等待 } } } ``` 2. 当线程终止时,JVM 自动调用 `notifyAll()` 唤醒等待线程(引用[1]) #### 六、注意事项 1. **死锁风险**:若两个线程互相调用 `join()` 会导致死锁 2. **中断处理**:需捕获 `InterruptedException` 3. **超时机制**:使用 `join(long millis)` 避免无限等待 4. **替代方案**:`CountDownLatch`/`CompletableFuture` 更适合复杂同步 > **应用场景示例**:主线程启动日志写入线程后,必须确保日志保存完毕再退出程序(引用[4])。 --- ### 相关问题 1. `join()` 方法在什么场景下会导致死锁?如何避免? 2. 除了 `join()`,还有哪些Java工具可以实现线程同步? 3. `join()` 和 `sleep()` 在阻塞线程时有何本质区别? 4. 如何通过 `CompletableFuture` 替代 `join()` 实现更灵活的线程控制? [^1]: Thread.join的作用和原理 [^2]: Java 中的 Thread.join() 方法详解 [^3]: Thread系列:join()方法的作用是什么 [^4]: java thread join方法_JAVA THREAD.JOIN方法详解
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值