线程等待 Join()方法

本文通过一个具体的Java示例介绍了如何使用线程的Join方法。Join方法可以使主线程等待子线程执行完毕后再继续执行,确保了线程间的执行顺序。文中还对比了使用与未使用Join方法时程序行为的不同。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • Join()方法是让一个线程等待另一个线程的完成
    这里写图片描述
    下面看个例子:
package project11;

public class TestJoin extends Thread {
    public void run() {
        for(int i= 1;i<=10;i++)//线程循环10次输出
        {
            System.out.println(Thread.currentThread().getName()+"第"+i+"次运行");
        }
        System.out.println(Thread.currentThread().getName()+"运行结束");
    }
    public static void main(String [] args) {   //main函数的主线程开始运行
        System.err.println("主线程开始运行");
        TestJoin tj = new TestJoin();   //创建线程类
        tj.setName("线程1");    //给线程起名字
        try {
            long start = System.nanoTime();
            tj.start();
            tj.join();  //等待tj线程运行结束
            long end = System.nanoTime();
            System.out.println("耗时"+(end-start)/1000000+"毫秒");
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    System.out.println("主线程运行结束");//main函数的主线程运行结束
    }



}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

“`这里写图片描述
从程序运行结果可以看出,main方法代表的主线程在建立TestJoin tj 这个线程对象后,调用t.start()来运行线程,并调用了tj.join()来等待线程tj的完成,在线程tj完成了10次字符输出并结束线程后,主线程才计算了线程tj的耗时,并输出字符然结束主线程的运行。
这里写图片描述
这样理解会简单一些
!!!!!把程序中的tj.join()这行程序注释掉,会发现
如果没有使用Join方法,那么主线程就会在tj线程还未运行结束前就结束。

转载于:https://www.cnblogs.com/cuihongyu3503319/p/9232274.html

<think>我们要求回答线程join方法。首先,我们需要解释join方法的作用、用法以及背后的原理。然后,提供代码示例展示如何使用join方法。最后,按照要求提出相关问题。 ### 回答结构: 1. **解释join方法**:说明join方法的作用是让一个线程等待另一个线程完成。 2. **使用场景**:当主线程需要等待线程执行完毕后再继续执行时使用。 3. **方法原理**:join方法内部使用wait/notify机制,当调用某个线程join方法时,当前线程等待直到目标线程终止。 4. **代码示例**:展示join方法的基本用法,包括带超时和不带超时的版本。 5. **注意事项**:提到join方法可能抛出InterruptedException,需要处理。 6. **相关问题**:按照格式提出四个相关问题。 ### 代码示例: ```java public class JoinExample { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { for (int i = 0; i < 5; i++) { System.out.println("Thread 1: " + i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 5; i++) { System.out.println("Thread 2: " + i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }); t1.start(); t2.start(); // 主线程等待t1线程执行完毕 t1.join(); System.out.println("Thread 1 has finished."); // 主线程等待t2线程执行完毕,最多等待2秒 t2.join(2000); System.out.println("Thread 2 has finished or timeout reached."); System.out.println("Main thread continues."); } } ``` ### 解释: - **t1.join()**:主线程等待t1线程执行完毕,然后再继续执行。 - **t2.join(2000)**:主线程最多等待t2线程2秒钟,如果2秒内t2没有执行完,主线程会继续执行。 ### 注意事项: - join方法会抛出InterruptedException,表示在等待过程中线程被中断,需要处理该异常。 - 如果线程已经终止,调用join方法会立即返回。 ### 相关问题: 按照要求,提出四个相关问题: 1. join方法的底层实现原理是什么? 2. 如果多个线程同时调用同一个线程join方法会怎样? 3. join方法和sleep方法有什么区别? 4. 如何在不使用join的情况下实现线程等待? ### 最终回答: 线程join方法详解 join方法是Thread类的一个实例方法,用于让当前线程(通常是主线程等待调用该方法线程(子线程)执行完毕。其核心作用是实现线程间的同步,确保某个线程完成后才继续执行后续代码。 #### 1. join方法的作用 - **阻塞调用线程**:当线程A调用线程B的join方法时,线程A会被阻塞,直到线程B结束。 - **同步控制**:常用于主线程需要等待线程处理完任务后再进行汇总或后续操作。 #### 2. 方法签名 ```java public final void join() throws InterruptedException public final synchronized void join(long millis) throws InterruptedException public final synchronized void join(long millis, int nanos) throws InterruptedException ``` - 无参join():无限期等待,直到目标线程结束。 - 带超时参数:最多等待指定时间,超时后无论目标线程是否结束,调用线程都会恢复运行。 #### 3. 实现原理 join方法内部使用wait/notify机制。当调用t.join()时,当前线程(假设是main线程)会获取t线程对象的锁(因为join是同步方法),然后调用wait()方法,进入等待状态。当t线程结束时,JVM会调用t线程对象的notifyAll()方法,唤醒所有等待在该对象上的线程(即main线程被唤醒),从而继续执行。 #### 4. 代码示例 ```java public class JoinDemo { public static void main(String[] args) throws InterruptedException { Thread worker = new Thread(() -> { System.out.println("Worker starts..."); try { Thread.sleep(3000); // 模拟耗时任务 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Worker finished."); }); worker.start(); System.out.println("Main thread waits for worker..."); worker.join(); // 主线程等待worker线程结束 System.out.println("Main thread resumes after worker."); } } ``` #### 5. 注意事项 - **中断处理**:join方法会响应中断,如果等待过程中当前线程被中断,会抛出InterruptedException。 - **线程状态**:如果目标线程尚未启动(start)或已经结束,调用join会立即返回。 - **锁机制**:由于join方法内部使用synchronized,因此要注意避免死锁。 §§相关问题§§: 1. join方法的底层实现原理是什么? 2. 如果多个线程同时调用同一个线程join方法会怎样? 3. join方法和sleep方法有什么区别? 4. 如何在不使用join的情况下实现线程等待?</think>### Java 线程的 `join()` 方法详解 #### 一、`join()` 方法的核心作用 `join()` 方法用于**强制当前线程等待目标线程执行结束**,实现线程间的同步协作。当线程 A 调用线程 B 的 `join()` 方法时,线程 A 会进入阻塞状态,直到线程 B 终止。 #### 二、方法签名及重载 ```java // 无限期等待目标线程结束 public final void join() throws InterruptedException // 最多等待 millis 毫秒 public final synchronized void join(long millis) throws InterruptedException // 精确等待(毫秒+纳秒) public final synchronized void join(long millis, int nanos) throws InterruptedException ``` #### 三、底层实现原理 `join()` 方法底层基于 `wait/notify` 机制实现: ```java public final synchronized void join(long millis) { // ... while (isAlive()) { // 检查目标线程是否存活 wait(millis); // 调用线程进入等待状态 } // ... } ``` 当目标线程终止时,JVM 会自动调用该线程对象的 `notifyAll()` 方法,唤醒所有等待在该对象上的线程。 #### 四、典型使用场景 1. **主线程等待线程完成** ```java public class JoinBasicDemo { public static void main(String[] args) throws InterruptedException { Thread worker = new Thread(() -> { System.out.println("Worker started"); try { Thread.sleep(2000); } catch (Exception ignored) {} System.out.println("Worker completed"); }); worker.start(); System.out.println("Main waiting for worker..."); worker.join(); // 主线程阻塞,直到worker结束 System.out.println("Main continues after worker"); } } ``` 2. **顺序执行多个线程** ```java Thread t1 = new Thread(() -> System.out.println("Task 1")); Thread t2 = new Thread(() -> System.out.println("Task 2")); Thread t3 = new Thread(() -> System.out.println("Task 3")); t1.start(); t1.join(); // 等待t1完成 t2.start(); t2.join(); // 等待t2完成 t3.start(); t3.join(); // 等待t3完成 ``` #### 五、带超时的 `join()` ```java public class JoinTimeoutDemo { public static void main(String[] args) throws InterruptedException { Thread slowTask = new Thread(() -> { try { Thread.sleep(5000); // 耗时5秒的任务 System.out.println("Slow task completed"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); slowTask.start(); System.out.println("Main waiting max 2 seconds..."); slowTask.join(2000); // 最多等待2秒 if (slowTask.isAlive()) { System.out.println("Timeout! Task not finished"); } else { System.out.println("Task finished in time"); } } } ``` #### 六、关键注意事项 1. **中断处理** `join()` 会抛出 `InterruptedException`,必须处理中断: ```java try { thread.join(); } catch (InterruptedException e) { // 恢复中断状态(重要!) Thread.currentThread().interrupt(); // 处理中断逻辑 } ``` 2. **调用时机** 必须在目标线程启动(`start()`)后调用 `join()`,否则立即返回: ```java Thread t = new Thread(/*...*/); t.join(); // 错误:线程未启动,立即返回 t.start(); // 正确顺序 ``` 3. **与 `synchronized` 的关系** `join()` 是同步方法,会获取目标线程对象的锁: ```java synchronized (thread) { while (thread.isAlive()) { thread.wait(0); } } ``` #### 七、`join()` 与替代方案对比 | **方法** | **适用场景** | **优点** | **缺点** | |--------------------|--------------------------------|-----------------------|-----------------------| | `join()` | 简单线程等待 | 原生支持,简单直接 | 无法处理复杂依赖关系 | | `CountDownLatch` | 多个线程等待一个事件 | 可重用,支持计数 | 需要额外对象 | | `CyclicBarrier` | 多线程相互等待 | 可重用,支持重置 | 实现复杂 | | `CompletableFuture`| 异步任务链式处理 | 功能强大,支持组合操作 | Java 8+ 才支持 | #### 八、底层源码关键逻辑 ```java // Thread.join() 核心实现(简化版) public final synchronized void join(long millis) { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) throw new IllegalArgumentException("timeout < 0"); if (millis == 0) { while (isAlive()) { // 无限期等待 wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) break; // 超时退出 wait(delay); now = System.currentTimeMillis() - base; } } } ``` #### 九、最佳实践建议 1. **优先使用超时版本**:避免永久阻塞导致系统僵死 2. **配合中断处理**:确保能响应取消请求 3. **复杂场景用并发工具**:超过 2 个线程的协作使用 `CountDownLatch` 等工具 4. **避免嵌套调用**:`join()` 中调用另一个 `join()` 容易导致死锁
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值