Java并发编程:使用Thread.join()等待线程终止
在多线程开发场景中,经常需要确保某些后台任务(如资源初始化、数据加载)完全结束后,主程序才进行下一步操作。本文将深入解析如何利用 Java 的 Thread.join() 方法实现这一需求,涵盖其原理、不同时间参数的行为差异以及具体的代码实现。
核心目标与准备条件
Thread.join() 是 Java 线程协作的关键机制之一。它的核心作用是:让当前线程(调用者)等待被调用对象(即该 Thread 实例所代表的线程)执行完毕后再继续运行。
- 应用场景:依赖外部资源初始化的服务启动、批量异步任务的聚合结果处理等。
- 前置条件:被调用的线程必须处于
RUNNABLE或BLOCKED状态(即已成功启动并正在执行),否则将直接返回。
真实操作顺序与代码实现
第1步:定义任务类并实现 Runnable 接口
为了实现具体的业务逻辑,我们创建两个模拟耗时操作的类。它们通过模拟休眠时间来演示线程的执行过程。注意,这里使用了 TimeUnit.SECONDS.sleep() 替代传统的 Thread.sleep(),语义更清晰。
// DataSourcesLoader.java
public class DataSourcesLoader implements Runnable {
@Override
public void run() {
System.out.printf("Beginning data sources loading: %s\n", new Date());
try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.printf("Data sources loading has finished: %s\n", new Date());
}
}
// NetworkConnectionsLoader.java
public class NetworkConnectionsLoader implements Runnable {
@Override
public void run() {
System.out.printf("Beginning network connections loading: %s\n", new Date());
try { TimeUnit.SECONDS.sleep(6); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.printf("Network connections loading has finished: %s\n", new Date());
}
}
第2步:构建主类并启动线程
在主流程中,我们将任务实例包装为 Thread 对象。为了保证线程名易于调试,建议在构造函数中指定名称。
public class Main {
public static void main(String[] args) {
// 1. 创建任务实例
DataSourcesLoader dsLoader = new DataSourcesLoader();
NetworkConnectionsLoader ncLoader = new NetworkConnectionsLoader();
// 2. 包装为 Thread 并启动
Thread thread1 = new Thread(dsLoader, "DataSourcesLoader");
Thread thread2 = new Thread(ncLoader, "NetworkConnectionsLoader");
thread1.start(); // 启动数据源加载线程
thread2.start(); // 启动网络连接加载线程
}
}
第3步:调用 join() 等待线程结束
这是实现“等待”逻辑的核心步骤。在启动线程后,主线程调用 thread1.join()。此时,主线程会被挂起(阻塞),直到 thread1 彻底运行完毕,或者在 thread1 中断时抛出异常。
try {
// 依次等待两个线程完成。实际中可改为并行等待逻辑(结合CompletableFuture)
// 此处严格按照顺序等待,若需并发等待两者结束,可使用 joinAll 或更现代的 API
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Main: Configuration has been loaded: %s\n", new Date());
完成判断标志: 当控制台打印出 “Configuration has been loaded” 时,说明所有 join() 等待的子线程均已正常退出。
进阶用法:加入超时参数
为了避免死锁或无限期的等待,JDK 提供了带有时间参数的重载方法:
-
join(long milliseconds)
等待指定的毫秒数,或者直到线程结束。如果在线程结束前超时,方法也会返回,主线程将继续执行后续逻辑(而非永久阻塞)。 -
join(long milliseconds, int nanos)
提供更精确的微秒级等待控制。例如thread1.join(1000, 0)表示最多等待1秒。
逻辑对比表
| 方法签名 | 行为描述 | 返回值时机 |
|---|---|---|
join() | 永久等待 | 线程自然结束或被中断 |
join(ms) | 限时等待 | 线程结束 或 超时到达 |
join(ms, ns) | 高精度限时等待 | 线程结束 或 超时到达 |
简要总结
Thread.join() 是解决简单线程间依赖的最直接工具。虽然它会导致调用线程阻塞,但在处理明确的先后依赖关系时,它是标准做法。对于更复杂的并发场景,建议结合 CountDownLatch 或 CompletableFuture 以获得更高的灵活性和非阻塞特性。
以上就是《Java并发编程示例(六):等待线程执行终止》的详细内容,更多关于《Java并发编程示例集》的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









