java中的join方法

本文详细解析了Java多线程中的Join方法工作原理及其使用方式,通过具体示例代码展示了不同参数下Join方法的行为表现,帮助读者深入理解线程间的同步机制。

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

     方法Join 是干啥用的? 简单回答,同步,如何同步? 怎么实现的? 下面将逐个回答。 
    自从接触Java 多线程,一直对Join 理解不了。JDK 是这样说的:join public final void join (long millis )throws InterruptedException Waits at most millis milliseconds for this thread to die. A timeout of 0 means to wait forever. 大家能理解吗? 字面意思是等待一段时间直到这个线程死亡,我的疑问是那个线程,是它本身的线程还是调用它的线程的,上代码: 
    package  concurrentstudy; 
    /** 
    * 
    *  @author  vma 
    */ 
    public   class  JoinTest { 
    public   static   void  main(String[] args) { 
    Thread t =  new  Thread( new  RunnableImpl()); 
    t.start(); 
    try  { 
    t.join(1000) ; // 主线程只等1 秒,不管子线程什么时候结束 
    System.out.println("joinFinish"); 
    }  catch  (InterruptedException e) { 
    // TODO Auto-generated catch block 
    e.printStackTrace(); 
    } 
    } 
    } 
    class  RunnableImpl  implements  Runnable { 
    @Override 
    public   void  run() { 
    try  { 
    System.out.println("Begin sleep"); 
    Thread.sleep(1000); 
    System.out.println("End sleep"); 
    }  catch  (InterruptedException e) { 
    e.printStackTrace(); 
    } 
    } 
    }

    结果是: 
    Begin sleep 
    End sleep 
    joinFinish 
    明白了吧, 当main 线程调用t.join 时,main 线程等待t 线程 ,等待时间是1000 ,如果t 线程Sleep 2000 呢 
    public   void  run() { 
    try  { 
    System.out.println("Begin sleep"); 
    // Thread.sleep(1000); 
    Thread.sleep(2000) ; 
    System.out.println("End sleep"); 
    }  catch  (InterruptedException e) { 
    e.printStackTrace(); 
    } 
    } 
   结果是: 
    Begin sleep 
    joinFinish 
    End sleep 
    也就是说 main 线程只等1000 毫秒,不管T 什么时候结束 ,如果是t.join() 呢, 看代码: 
    public final void join() throws InterruptedException { 
    join(0); 
    } 
    就是说如果是t.join() = t.join(0) JDK 这样说的 A timeout of  0  means to wait forever 字面意思是永远等待,是这样吗? 
    其实是等到t 结束后。 
    这个是怎么实现的吗? 看JDK 代码: 
    /** 
    * 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 any 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) 
    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 (小提示:Object 提供的方法)。 当main 线程调用t.join 时候,main 线程会获得线程对象t 的锁 (wait 意味着拿到该对象的锁), 调用该对象的wait( 等待时间) ,直到该对象唤醒main 线程,比如退出后。 
    这就意味着main 线程调用t.join 时,必须能够拿到线程t 对象的锁 ,如果拿不到它是无法wait 的,刚开的例子t.join(1000) 不是说明了main 线程等待1 秒,如果在它等待之前,其他线程获取了t 对象的锁,它等待时间可不就是1 毫秒了。上代码介绍: 
    /* 
    * To change this template, choose Tools | Templates 
    * and open the template in the editor. 
    */ 
    package  concurrentstudy; 
    /** 
    * 
    *  @author  vma 
    */ 
    public   class  JoinTest { 
    public   static   void  main(String[] args) { 
    Thread t =  new  Thread( new  RunnableImpl()); 
    new  ThreadTest(t).start();// 这个线程会持有锁 
    t.start(); 
    try  { 
    t.join(); 
    System.out.println("joinFinish"); 
    }  catch  (InterruptedException e) { 
    // TODO Auto-generated catch block 
    e.printStackTrace(); 
    } 
    } 
    } 
    class  ThreadTest  extends  Thread { 
    Thread thread; 
    public  ThreadTest(Thread thread) { 
    this .thread = thread; 
    } 
    @Override 
    public   void  run() { 
    holdThreadLock(); 
    } 
    public   void  holdThreadLock() { 
    synchronized  (thread) { 
    System.out.println("getObjectLock"); 
    try  { 
    Thread.sleep(9000); 
    }  catch  (InterruptedException ex) { 
    ex.printStackTrace(); 
    } 
    System.out.println("ReleaseObjectLock"); 
    } 
    } 
    } 
    class  RunnableImpl  implements  Runnable { 
    @Override 
    public   void  run() { 
    try  { 
    System.out.println("Begin sleep"); 
    Thread.sleep(2000); 
    System.out.println("End sleep"); 
    }  catch  (InterruptedException e) { 
    e.printStackTrace(); 
    } 
    } 
    } 
    在main 方法中   通过new  ThreadTest(t).start(); 实例化ThreadTest   线程对象, 它在 holdThreadLock() 方法中,通过   synchronized  (thread) ,获取线程对象t 的锁,并Sleep (9000 )后释放,这就意味着,即使 
    main 方法t.join(1000), 等待一秒钟,它必须等待 ThreadTest   线程释放t 锁后才能进入wait 方法中,它实际等待时间是9000+1000 MS 
   运行结果是: 
    getObjectLock 
    Begin sleep 
    End sleep 
    ReleaseObjectLock

    joinFinish

<think>我们正在讨论Java中Thread类的join方法。根据引用资料,join方法的主要作用是让一个线程等待另一个线程完成。下面我将详细解释join方法的作用、工作原理、使用方式以及应用场景。 ### 一、join方法的作用 1. **线程等待**:当在某个线程(如主线程)中调用`thread.join()`时,当前线程会进入等待状态,直到目标线程(即thread)执行完毕[^1][^2]。 2. **协调执行顺序**:通过join方法,可以控制多个线程的执行顺序,确保某些线程在其他线程之前完成[^4]。 ### 二、join方法的工作原理 join方法底层是基于Java的等待/通知机制(wait/notify)实现的: 1. 当调用`thread.join()`时,当前线程(假设是主线程)会获取thread对象的(因为join方法内部使用`synchronized`修饰)[^2]。 2. 然后检查thread是否存活(通过`isAlive()`方法),如果存活,则调用`wait()`方法使当前线程进入等待状态[^2]。 3. 当目标线程thread执行完毕(进入TERMINATED状态)时,JVM会自动调用thread的`notifyAll()`方法,唤醒所有等待在该thread对象上的线程[^2]。 ```java // join方法源码片段(Java 17) public final void join() throws InterruptedException { join(0); } public final synchronized void join(long millis) throws InterruptedException { // 检查当前线程是否已被中断 if (Thread.interrupted()) { throw new InterruptedException(); } // 如果超时时间为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; } } } ``` ### 三、join方法的使用方式 #### 1. 基本用法:等待单个线程结束 ```java Thread worker = new Thread(() -> { // 模拟耗时操作 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("子线程执行完毕"); }); worker.start(); // 主线程等待worker结束 worker.join(); // 主线程在此阻塞,直到worker运行结束[^1] System.out.println("主线程继续执行"); ``` #### 2. 等待多个线程结束 ```java List<Thread> threads = new ArrayList<>(); for (int i = 0; i < 5; i++) { Thread t = new Thread(/* 任务 */); t.start(); threads.add(t); } // 主线程等待所有子线程结束 for (Thread t : threads) { t.join(); // 依次等待每个线程结束[^3] } System.out.println("所有子线程已结束"); ``` #### 3. 带超时的等待 ```java Thread worker = new Thread(/* 长时间任务 */); worker.start(); worker.join(3000); // 最多等待3秒[^5] if (worker.isAlive()) { System.out.println("worker仍在运行,主线程不再等待"); // 可选项:中断worker线程 worker.interrupt(); } ``` ### 四、应用场景 1. **结果依赖** 主线程需要获取子线程的处理结果时(如计算值、状态等),必须等待子线程结束[^4]。 ```java class ResultHolder { int result; } ResultHolder holder = new ResultHolder(); Thread calculator = new Thread(() -> { holder.result = compute(); // 耗时计算 }); calculator.start(); calculator.join(); // 等待计算完成 System.out.println("计算结果:" + holder.result); ``` 2. **资源有序释放** 需要确保子线程释放资源后,主线程才能继续操作(如文件句柄、网络连接)[^4]。 ```java Thread fileProcessor = new Thread(() -> { try (FileInputStream fis = new FileInputStream("data.txt")) { // 处理文件 } }); fileProcessor.start(); fileProcessor.join(); // 确保文件已关闭 // 安全删除文件 Files.delete(Paths.get("data.txt")); ``` 3. **任务分阶段执行** 多阶段任务中,下一阶段必须等待前一阶段的所有子任务完成[^3]。 ```java // 第一阶段:数据加载 List<Thread> loadTasks = startDataLoadTasks(); for (Thread t : loadTasks) t.join(); // 第二阶段:数据处理 List<Thread> processTasks = startDataProcessTasks(); for (Thread t : processTasks) t.join(); // 第三阶段:结果汇总 aggregateResults(); ``` ### 五、注意事项 1. **中断处理** 若等待过程中当前线程被中断(如调用`interrupt()`),`join()`会立即抛出`InterruptedException`并清除中断状态[^5]。 ```java try { worker.join(); } catch (InterruptedException e) { // 中断处理:清理资源并退出 Thread.currentThread().interrupt(); // 恢复中断状态 } ``` 2. **释放** 调用`join()`不会释放当前线程持有的其他对象(因为join方法只获取目标线程对象的)[^2]。 3. **性能影响** 过度使用join可能导致线程串行化,失去并发优势。此时可考虑`java.util.concurrent`包中的高级工具(如`CountDownLatch`、`CyclicBarrier`)[^1]。 ```mermaid graph TD A[主线程调用t.join] --> B{线程t是否存活?} B -->|是| C[主线程进入WAITING状态] B -->|否| D[主线程继续执行] C --> E[线程t终止] E --> F[JVM调用t.notifyAll] F --> D ``` > **最佳实践**:优先使用`java.util.concurrent`工具替代原生join,尤其在需要等待多个线程的场景。 §§相关问题§§ 1. 为什么`join()`方法会导致调用线程阻塞? 2. `join()`与`sleep()`在阻塞线程时有何本质区别? 3. 如何用`CountDownLatch`替代`join()`实现更灵活的线程等待? 4. 调用`join()`后主线程被中断会发生什么?</think>### Java Thread.join() 方法详解及使用场景 #### 一、核心作用 `join()` 是 Java `Thread` 类的关键方法,用于**线程同步控制**: 1. **阻塞等待**:调用线程(如主线程)会阻塞,直到目标线程执行完毕[^1][^2] 2. **执行顺序管理**:确保多个线程按特定顺序执行[^3] 3. **结果依赖**:主线程获取子线程处理结果前等待其完成[^4] #### 二、工作原理 ```mermaid graph TD A[主线程调用 thread.join] --> B{目标线程是否存活?} B -->|是| C[主线程进入WAITING状态] B -->|否| D[主线程继续执行] C --> E[目标线程终止] E --> F[JVM调用线程的notifyAll] F --> G[主线程被唤醒] ``` 底层实现基于**等待/通知机制**: ```java // 简化版源码实现 public final synchronized void join(long millis) { while (isAlive()) { // 检查目标线程是否存活 wait(millis); // 调用线程进入等待状态 } } ``` #### 三、使用方式 ##### 1. 基础用法:等待单个线程 ```java Thread worker = new Thread(() -> { System.out.println("子线程开始工作"); try { Thread.sleep(2000); } catch (InterruptedException e) { /* 处理中断 */ } System.out.println("子线程结束"); }); worker.start(); worker.join(); // 主线程在此阻塞,直到worker结束[^1] System.out.println("主线程继续执行"); ``` **输出**: ``` 子线程开始工作 (等待2秒) 子线程结束 主线程继续执行 ``` ##### 2. 等待多个线程 ```java List<Thread> threads = new ArrayList<>(); for (int i = 0; i < 3; i++) { Thread t = new Thread(/* 任务逻辑 */); t.start(); threads.add(t); } // 主线程等待所有子线程结束 for (Thread t : threads) { t.join(); // 依次等待每个线程[^3] } System.out.println("所有子线程已完成"); ``` ##### 3. 带超时控制 ```java Thread worker = new Thread(/* 长时间任务 */); worker.start(); worker.join(3000); // 最多等待3秒[^5] if (worker.isAlive()) { System.out.println("超时,强制中断"); worker.interrupt(); // 中断仍在运行的线程 } ``` #### 四、应用场景 1. **结果依赖** 主线程需要子线程计算结果时: ```java class ResultHolder { int result; } ResultHolder holder = new ResultHolder(); Thread calculator = new Thread(() -> { holder.result = compute(); // 耗时计算 }); calculator.start(); calculator.join(); // 确保结果已计算 System.out.println("结果:" + holder.result); // [^4] ``` 2. **资源有序释放** 确保子线程释放资源后主线程继续: ```java Thread fileProcessor = new Thread(() -> { try (FileInputStream fis = new FileInputStream("data.txt")) { // 处理文件 } }); fileProcessor.start(); fileProcessor.join(); // 确保文件已关闭 Files.delete(Paths.get("data.txt")); // 安全删除 ``` 3. **任务分阶段执行** 多阶段任务控制: ```java // 第一阶段:数据加载 Thread dataLoader = new Thread(loadDataTask); dataLoader.start(); dataLoader.join(); // 第二阶段:数据处理(依赖已加载数据) Thread dataProcessor = new Thread(processDataTask); dataProcessor.start(); ``` #### 五、注意事项 1. **中断处理** 若等待中被中断,会抛出 `InterruptedException`: ```java try { worker.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 恢复中断状态 // 清理逻辑 } ``` 2. **机制** `join()` 使用 `synchronized` 关键字,但**不会释放调用线程持有的其他**(仅获取目标线程对象的)[^2]。 3. **性能替代方案** 对于复杂同步需求,优先使用 `java.util.concurrent` 工具: ```java // 使用CountDownLatch替代join CountDownLatch latch = new CountDownLatch(3); ExecutorService executor = Executors.newFixedThreadPool(3); for (int i = 0; i < 3; i++) { executor.submit(() -> { try { /* 任务 */ } finally { latch.countDown(); } }); } latch.await(); // 等待所有任务完成 executor.shutdown(); ``` > **最佳实践**: > - 优先使用线程池(`ExecutorService`)管理线程 > - 需要简单等待时用 `join()`,复杂同步用 `CountDownLatch/CyclicBarrier` > - 始终处理 `InterruptedException`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值