Thread 的join方法源码解读

本文详细阐述了Java Thread类中的join方法如何实现线程同步,解释了其在并发编程中的作用,以及join与start调用顺序的重要性。通过实例和源码解析,展示了join方法在串行执行和时间等待上的应用。

一、作用

  Thread类中的join方法的主要作用就是同步,它可以使得线程之间的并行执行变为串行执行。具体看代码:

public class JoinTest {
    public static void main(String [] args) throws InterruptedException {
        ThreadJoinTest t1 = new ThreadJoinTest("小明");
        ThreadJoinTest t2 = new ThreadJoinTest("小东");
        t1.start();
        /**join的意思是使得放弃当前线程的执行,并返回对应的线程,例如下面代码的意思就是:
         程序在main线程中调用t1线程的join方法,则main线程放弃cpu控制权,并返回t1线程继续执行直到线程t1执行完毕
         所以结果是t1线程执行完后,才到主线程执行,相当于在main线程中同步t1线程,t1执行完了,main线程才有执行的机会
         */
        t1.join();
        t2.start();
    }

}
class ThreadJoinTest extends Thread{
    public ThreadJoinTest(String name){
        super(name);
    }
    @Override
    public void run(){
        for(int i=0;i<5;i++){
            System.out.println(this.getName() + ":" + i);
        }
    }
}

输出结果:

上面注释也大概说明了join方法的作用:在A线程中调用了B线程的join()方法时,表示只有当B线程执行完毕时,A线程才能继续执行,相当于将并行变成了串行。注意,这里调用的join方法是没有传参的,join方法其实也可以传递一个参数给它的。

join方法中如果传入参数,则表示这样的意思:如果A线程中掉用B线程的join(10),则表示A线程会等待B线程执行10毫秒,10毫秒过后,A、B线程并行执行,但是具体怎么执行,还得看cpu的调度。需要注意的是,jdk规定,join(0)的意思不是A线程等待B线程0秒,而是A线程等待B线程无限时间,直到B线程执行完毕,即join(0)等价于join()。

二、join与start调用顺序问题

  上面的讨论大概知道了join的作用了,那么,入股 join在start前调用,会出现什么后果呢?先看下面的测试结果:

public class JoinTest {
    public static void main(String [] args) throws InterruptedException {
        ThreadJoinTest t1 = new ThreadJoinTest("小明");
        ThreadJoinTest t2 = new ThreadJoinTest("小东");
        /**join方法可以在start方法前调用时,并不能起到同步的作用
         */
        t1.join();
        t1.start();
        //Thread.yield();
        t2.start();
    }

}
class ThreadJoinTest extends Thread{
    public ThreadJoinTest(String name){
        super(name);
    }
    @Override
    public void run(){
        for(int i=0;i<1000;i++){
            System.out.println(this.getName() + ":" + i);
        }
    }
}

输出结果:

所以得到以下结论:join方法必须在线程start方法调用之后调用才有意义。这个也很容易理解:如果一个线程都没有start,那它也就无法同步了。

三、源码解析

不多废话,直接上源码:

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

默认的join方法是传入一个0毫秒的,接下看实现:

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;
            }
        }
    }

先关注下方法修饰词,使用final修饰,表示此方法不能重写,无法覆盖,synchronized在JVM级别维护了线程的同步。接下看这个isAlive,我们去看注释:

/**
     * Tests if this thread is alive. A thread is alive if it has
     * been started and has not yet died.
     *
     * @return  <code>true</code> if this thread is alive;
     *          <code>false</code> otherwise.
     */
    public final native boolean isAlive();

线程只有started(启动)而且还没有销毁的时候isAlive这个状态才为true,换句话说,一个线程需要调用了start()方法,而且没有死亡(阻塞的线程调用了interrupt()方法,或者线程执行完毕),在这中间的状态才是true,然后调用JNI的wait()方法,使用等待-通知机制去进行线程间的交互。

public final native void wait(long timeout) throws InterruptedException;

本次实例中,我们也可以加上一行输出语句验证,结果和我们要的一样。

参考:https://blog.youkuaiyun.com/si444555666777/article/details/82347056

 

<think>首先,用户的问题是关于Java中join()方法的使用及原理的综合信息。我需要基于系统指令和提供的引用资料来构建回答。 系统指令回顾: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 用户可见层要求: - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 用户查询:"我想了解Java中join()方法的相关信息 请问Java中join()方法的使用及原理" 提供的引用: - [^1]: 关于join()方法的使用方法源码分析、等效替换方法、响应中断和数据一致性。 - [^2]: 展示join()方法的示例代码,说明它控制线程执行顺序。 - [^3]: 提供了join(long millis)的源码。 - [^4]: 详细解读join()方法,包括方法签名、基础示例和线程同步机制。 我需要综合这些引用,构建一个连贯的回答。回答结构应该清晰,逐步解释使用和原理。 大纲回答结构: 1. **引言**:简要介绍join()方法的用途,即线程同步。 2. **方法定义**:解释方法签名和参数。 3. **基本使用**:通过示例代码展示如何使用。 4. **原理分析**:解释内部工作原理,包括源码分析。 5. **注意事项**:提到中断响应和数据一致性。 6. **替代方法**:简要说明等效替换方法。 7. **总结**:重申关键点。 8. **相关问题**:生成相关问题。 确保使用中文回答。引用标识:在解释中自然添加引用标识,例如在描述特定点时引用来源。 例如: - 引用[^1]提到join()方法用于维护数据一致性。 - 引用[^3]展示了源码实现。 现在,针对数学表达式:用户查询是关于Java编程的,没有明显的数学表达式。但系统指令说所有行内数学表达式必须用$...$。这里可能不需要,但如果有比如时间计算,可以用。例如,在解释源码时,如果有数学表达式,使用$...$。但在这个上下文中,可能不适用。如果提到等待时间,可以用$time$,但应避免强行添加。指令说"包括变量、不等式、方程等",但在这个主题中较少见。所以,我不添加不必要的数学表达式。 独立公式:同样,如果没有必要,不使用。 生成相关问题:结尾添加相关问题。 引用标识:在句子末尾自然添加引用标识。 回答框架: - **开头**:直接回答问题。 - **主体**: - 定义和用途。 - 使用示例。 - 原理(源码分析)。 - 注意事项。 - **结尾**:总结。 - **相关问题**。 详细内容: 1. **定义和用途**:join()方法用于使一个线程等待另一个线程结束。例如,主线程等待子线程完成执行后才继续。引用[^4]描述了它在多线程同步中的应用。 2. **方法签名**:public final void join() throws InterruptedException; 其他重载版本支持超时。 3. **基本使用**:使用引用[^2]和[^4]的示例代码。解释代码。 - 示例:创建线程,启动,调用join()确保主线程等待。 4. **原理**:基于引用[^3]的源码。解释wait()和isAlive()的使用。核心是:调用join()时,如果线程还活着,当前线程等待,直到目标线程结束。 - 源码解析:join()方法内部调用wait(),当目标线程结束时,它会notifyAll()。 5. **注意事项**:响应中断(InterruptedException),引用[^1]提到维护数据一致性。 6. **等效替换**:引用[^1]说有两种等效替代方法。简要描述,如使用synchronized或CountDownLatch。 7. **总结**:强调join()在控制线程顺序中的作用。 使用Markdown格式使结构清晰: - 代码块用于示例代码。 - 加粗标题。 最后,生成相关问题: 1. join()方法与sleep()的区别是什么? 2. 如何处理join()方法中的中断? 3. 有哪些替代join()的线程同步机制? 4. join()方法在分布式系统中的适用性? 确保语言专业、清晰。 </think>### Java中join()方法的使用及原理详解 在Java多线程编程中,`join()`方法是一种关键的线程同步机制,用于协调线程执行顺序。它确保调用线程(如主线程)等待目标线程(如子线程)结束后才继续执行,这在数据处理、任务依赖等场景中至关重要。下面我将基于引用资料,逐步解释其使用方法和原理,确保结构清晰、真实可靠。 --- #### 1. **方法定义与基本用途** - `join()`方法定义在`Thread`类中,有三种重载形式: - `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`:支持纳秒级精度(较少使用)。 - **核心用途**:实现线程顺序控制。例如,主线程需等待所有子线程完成任务后再汇总结果,避免数据不一致或不完整[^4]。 #### 2. **基本使用示例** 以下是一个简单示例,展示如何在主线程中使用`join()`等待子线程完成: ```java public class JoinDemo { public static void main(String[] args) throws InterruptedException { Thread worker = new Thread(() -> { System.out.println("子线程开始工作"); try { Thread.sleep(2000); // 模拟耗时任务 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("子线程工作完成"); }); worker.start(); System.out.println("主线程等待子线程"); worker.join(); // 主线程在此等待worker结束 System.out.println("主线程继续执行"); } } ``` **输出结果**: ``` 主线程等待子线程 子线程开始工作 子线程工作完成 主线程继续执行 ``` - **解释**:主线程调用`worker.join()`后暂停执行,直到`worker`线程完成`sleep`任务。这保证了“主线程继续执行”在子线程结束后打印,确保线程顺序[^2][^4]。 #### 3. **原理分析:源码剖析** `join()`的核心原理基于Java的`wait()`和`notify()`机制。以下是`join(long millis)`的源码简化分析(引用自[^3]): ```java 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); // 调用Object.wait()释放锁 } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); // 超时等待 now = System.currentTimeMillis() - base; } } } ``` - **关键点**: - `isAlive()`检查目标线程是否存活:如果存活,则当前线程进入等待状态。 - `wait()`方法释放锁,暂停当前线程;当目标线程结束时,它会调用`notifyAll()`唤醒所有等待线程。 - **同步机制**:`join()`方法被`synchronized`修饰,确保线程安全。目标线程结束时,JVM内部会触发线程终止通知,唤醒等待线程[^3][^4]。 - **数学模型简化**:等待时间$t_{\text{wait}}$由初始时间$t_{\text{base}}$动态计算得出,公式为$t_{\text{wait}} = \text{millis} - (\text{now} - t_{\text{base}})$,确保超时逻辑精确[^3]。 #### 4. **注意事项与最佳实践** - **响应中断**:`join()`会抛出`InterruptedException`,需在调用时处理中断。忽略中断可能导致数据不一致或死锁。示例: ```java try { thread.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 恢复中断状态 System.out.println("主线程被中断,需清理资源"); } ``` - 这维护了多线程数据一致性,避免部分更新问题[^1]。 - **等效替代方法**:引用[^1]提到两种替代方案: 1. **使用`synchronized`和`wait()/notify()`**:手动实现类似逻辑。 2. **`CountDownLatch`工具**:更灵活的同步器,适合多线程协同。 - 示例等效代码(简化): ```java synchronized (thread) { while (thread.isAlive()) { thread.wait(); // 等效于join() } } ``` - **性能考虑**:无限期`join()`可能导致死锁。建议优先使用超时版本,如`join(5000)`,设置合理超时时间$t_{\text{timeout}}$以避免阻塞[^1][^4]。 #### 5. **总结** `join()`方法是Java多线程同步的基础工具,通过内部`wait()`机制实现线程等待,确保执行顺序和数据完整性。关键要点: - **使用场景**:主线程等待子线程完成(如任务分治)。 - **优点**:简单易用,无需额外库。 - **缺点**:不适合复杂同步;需处理中断。 合理使用`join()`能有效提升多线程程序的可靠性[^1][^4]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值