Thread之join方法

本文详细介绍了Java中线程的join方法的使用方法及其与synchronized、sleep的区别。通过具体的代码示例展示了join方法如何使主线程等待子线程的完成,并探讨了join方法在异常处理中的行为。


前言

在很多情况下,都由主线程创建并运行子线程,如果子线程中需要大量的耗时操作,主线程往往早于子线程结束。
如果主线程想等待子线程执行完成后再执行。比如子线程处理一个数据运算,而主线程想获取这个数据,可以用join方法。

public final void join() throws InterruptedException
等待该线程终止。
抛出:
InterruptedException - 如果任何线程中断了当前线程。当抛出该异常时,当前线程的中断状态被清除。

join()

public class Demo15 {
    public static void main(String[] args) throws InterruptedException {
        Demo15Thread t = new Demo15Thread();
        t.start();
        System.out.println("等待子线程运算");
        //等待该线程结束
        t.join();
        System.out.println("主线程结束");
    }
}
class Demo15Thread extends  Thread{
    @SneakyThrows
    @Override
    public void run() {
        //模拟大强度运算
        Thread.sleep(1500);
        System.out.println("子线程计算的值为: "+9584623333135895L);
    }
}

结论

在这里插入图片描述

join与synchronized

join内部使用wait()进行等待,而synchronized内部使用【对象锁】进行同步。

join与sleep

public class Demo15 {
    public static void main(String[] args) throws InterruptedException {
        Demo15ThreadA threadA = new Demo15ThreadA();
        Demo15ThreadB threadB = new Demo15ThreadB(threadA);
        Demo15ThreadC threadC = new Demo15ThreadC(threadA);

        threadB.start();
        threadC.start();
    }
}
class Demo15ThreadA extends  Thread{
    @SneakyThrows
    @Override
    public void run() {
        System.out.println("线程A开始于:" + System.currentTimeMillis());
        Thread.sleep(1000);
        System.out.println("线程A结束于:" + System.currentTimeMillis());
    }
    public synchronized void getTime(){
        System.out.println("方法执行时间: "+System.currentTimeMillis());
    }
}
class Demo15ThreadB extends  Thread{
    private Demo15ThreadA threadA;
    public Demo15ThreadB(Demo15ThreadA threadA){
        this.threadA = threadA;
    }
    @SneakyThrows
    @Override
    public void run() {
        synchronized (threadA){
            threadA.start();
            //sleep会导致线程阻塞,不会释放锁
            Thread.sleep(2000L);
            //join(long)内部使用wait(long),所以它会释放同步锁
            //threadA.join(2000L);
            while(true){

            }
        }
    }
}
class Demo15ThreadC extends  Thread{
    private Demo15ThreadA threadA;
    public Demo15ThreadC(Demo15ThreadA threadA){
        this.threadA = threadA;
    }
    @Override
    public void run() {
        threadA.getTime();
    }
}

结论

  • join内部使用wait()进行等待,而synchronized内部使用【对象锁】进行同步。
  • sleep(long)会导致线程阻塞,不会释放同步锁,join(long)内部使用wait(long),释放同步锁

join与异常

public class Demo16 {
    public static void main(String[] args) throws InterruptedException {
        Demo16ThreadB threadB = new Demo16ThreadB();
        threadB.start();
        //使threadB优先执行
        Thread.sleep(300);
        Demo16ThreadC threadC = new Demo16ThreadC(threadB);
        threadC.start();
    }
}
class Demo16ThreadA extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            // 模拟耗时操作
            String s = new String();
            Math.random();
        }
    }
}
class Demo16ThreadB extends Thread{
    @Override
    public void run() {
        try {
            Demo16ThreadA t = new Demo16ThreadA();
            t.start();
            //t线程加入到Demo16ThreadB中
            t.join();
            System.out.println("Demo16ThreadB线程正常结束");
        } catch (InterruptedException e) {
            System.out.println("Demo16ThreadB线程异常结束");
            e.printStackTrace();
        }
    }
}
class Demo16ThreadC extends Thread{
    private Demo16ThreadB threadB;
    public Demo16ThreadC(Demo16ThreadB threadB){
        this.threadB = threadB;
    }
    @Override
    public void run() {
        //模拟InterruptedException,中断Demo16ThreadB
        threadB.interrupt();
    }
}

运行结果

在这里插入图片描述

结论

尽管Demo16ThreadB被异常终端,但是Demo16ThreadA依然在正常运行。

<think>嗯,我现在要理解一下线程的join方法。首先,线程在编程中是用来实现并发执行的,对吧?比如在Python里,我可以创建一个线程来执行某个函数,然后主线程继续做其他事情。但是有时候,我需要等待这个子线程完成之后,主线程才能继续下去。这时候就需要用到join方法了。那join方法具体是怎么工作的呢? 我记得之前学过,当调用一个线程的join方法时,主线程会被阻塞,直到那个被调用的线程执行完毕。比如,假设我有两个线程t1和t2,如果我调用了t1.join(),那么主线程会在那里等着,直到t1完成它的任务,然后再继续执行后面的代码。不过,如果线程已经完成了,调用join会立即返回吗?比如,如果我在启动t1之后,主线程先做了其他事情,等过了一段时间再调用t1.join(),这时候如果t1已经结束了,join会不会立刻返回,而不会阻塞主线程? 另外,join方法有没有参数?比如超时时间。如果设置了超时时间,主线程最多等待多少秒,然后不管子线程有没有完成都会继续执行?比如t1.join(5)表示主线程最多等5秒,之后即使t1没完成也会继续。这时候,如何判断线程是否已经结束呢?可能需要检查线程的is_alive()方法? 还有,多个线程的join调用顺序会影响程序的执行吗?比如,主线程依次调用t1.join()和t2.join(),那么主线程会先等t1完成,再等t2完成。但是如果t1和t2是并行运行的,可能t2先于t1结束,这时候主线程在调用t1.join()时,t1可能已经结束了,所以主线程不会被阻塞,直接继续处理t2.join(),而t2可能已经结束,所以也不会阻塞。那在这种情况下,调用join的顺序是否会影响整体的等待时间? 举个例子,假设有两个线程,分别需要运行3秒和5秒。如果主线程先调用t1.join(),然后t2.join(),那么总等待时间大概是5秒,因为t1和t2是同时运行的,主线程在等待t1的3秒后,可能t2已经运行了3秒,还需要再等2秒,所以总共最多5秒?或者主线程会因为先等待t1完成,再等待t2,所以总时间会是max(t1, t2)的时间?这个时候可能需要画个时间线来理解。 还有,如果不调用join方法,主线程会不会在子线程还没完成时就结束,导致子线程被强制终止?比如在Python中,主线程结束后,如果还有子线程在运行,整个程序会等待所有子线程完成吗?或者说,主线程结束后,子线程会被后台挂起?可能不同的编程语言有不同的处理方式,但这里问题可能是关于通用的概念,或者是特定于某种语言比如Python的? 在Python的threading模块中,主线程会等待所有非守护线程完成后再退出。而守护线程(daemon线程)会在主线程结束时被强制终止。所以,如果创建的线程是默认的非守护线程,那么即使主线程不调用join,它也会等待所有子线程完成。但是,如果需要控制主线程在某个点等待子线程完成,就必须使用join方法。比如,主线程在某个任务之后需要子线程的结果,这时候就必须调用join来确保子线程已经完成了。 那join方法的使用场景通常是在主线程需要等待子线程处理完数据后,再进行后续操作。例如,主线程启动多个子线程处理不同的数据块,然后等待所有子线程处理完毕,再汇总结果。这时候,主线程需要依次调用每个子线程的join方法,或者使用其他同步机制,比如线程池或者屏障。 还有一个问题,如果在一个线程上多次调用join方法,会不会有问题?比如,先调用了t1.join(),然后又调用一次t1.join(),这时候会发生什么?根据Python的文档,线程一旦完成,调用join应该立即返回,所以多次调用应该没问题,只是第一次之后调用会直接返回。 总结一下,线程的join方法主要用于让主线程等待子线程结束,确保线程之间的同步。它可以带有超时参数,控制最大等待时间。使用join方法可以避免主线程提前退出导致子线程被意外终止,或者在需要子线程结果时保证数据就绪。</think>线程的 `join` 方法多线程编程中用于同步线程行为的关键方法。以下是逐步说明: --- ### **1. 核心作用** - **阻塞调用线程**:当线程 A 调用线程 B 的 `join()` 方法时,线程 A 会被阻塞,直到线程 B 执行完毕。 - **同步控制**:确保线程间的顺序执行。例如,主线程需等待子线程完成后再继续后续操作。 --- ### **2. 使用方式** - **基本语法**: ```python thread = Thread(target=task) thread.start() thread.join() # 主线程在此等待 thread 完成 ``` - **带超时参数**: ```python thread.join(timeout=5) # 最多等待5秒,超时后主线程继续执行 ``` --- ### **3. 关键特性** - **线程状态无关**:若线程已结束,调用 `join()` 会立即返回,不阻塞。 - **多次调用安全**:允许多次调用 `join()`,不会引发异常。 - **超时机制**:通过 `timeout` 参数控制最长等待时间,超时后需通过 `is_alive()` 检查线程状态: ```python thread.join(2) if thread.is_alive(): print("线程未完成,继续其他操作") ``` --- ### **4. 典型场景** #### **场景1:主线程等待子线程** ```python import threading def task(): print("子线程开始") # 模拟耗时操作 time.sleep(3) print("子线程结束") t = threading.Thread(target=task) t.start() t.join() # 主线程等待子线程完成 print("主线程继续") ``` **输出**: ``` 子线程开始 (等待3秒) 子线程结束 主线程继续 ``` #### **场景2:多线程并行后汇总结果** ```python threads = [] results = [] def worker(i): results.append(i * 2) for i in range(5): t = threading.Thread(target=worker, args=(i,)) threads.append(t) t.start() for t in threads: t.join() # 等待所有线程完成 print(results) # 输出所有结果:[0, 2, 4, 6, 8] ``` --- ### **5. 注意事项** - **守护线程(Daemon Thread)**:若子线程是守护线程(`daemon=True`),主线程结束时会强制终止它。此时 `join()` 可确保主线程等待其完成。 - **死锁风险**:避免循环等待(如线程 A 等待线程 B,线程 B 又等待线程 A)。 - **性能权衡**:过度使用 `join()` 可能降低并发效率,需根据实际需求选择同步机制。 --- ### **6. 不同语言的差异** - **Python**:主线程默认等待所有非守护线程完成,但 `join()` 仍常用于显式同步。 - **Java**:`thread.join()` 行为类似,但需处理 `InterruptedException`。 - **C++**:通过 `std::thread::join()` 实现类似功能,未调用会导致程序终止时报错。 --- ### **总结** `join` 方法通过阻塞调用线程来实现同步,确保线程执行顺序或数据就绪性。合理使用 `timeout` 参数和状态检查,可平衡程序的并发效率与逻辑正确性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值