推荐使用 ExecutorService + Callable 或 CompletableFuture:前者适用于需返回值和异常处理的场景,后者适合异步编排;手写 Thread 子类仅限教学或极简场景,实际项目中应重视线程池配置细节。

直接继承 Thread 类:简单但不推荐
这是最直观的创建方式,定义一个类继承 Thread,重写 run() 方法,然后调用 start() 启动。
问题在于 Java 不支持多重继承,一旦你的业务类已经继承了其他父类,这条路就走不通;而且线程逻辑和业务逻辑耦合过紧,不利于复用和测试。
-
start()才真正启动新线程;直接调用run()只是普通方法执行,不会开启线程 - 每个任务都需要新建一个子类,无法复用已有线程资源
- 无法返回执行结果,也不方便捕获异常(异常会直接抛到
Thread的未捕获处理器)
实现 Runnable 接口:更灵活的基础方案
把任务逻辑封装进 Runnable 实现类,再传给 Thread 构造器。这是解耦的第一步,也是最常用的起点。
它规避了继承限制,也更适合面向接口编程。但仍有局限:不能返回值、不能抛受检异常、无法获取执行状态。
立即学习“Java免费学习笔记(深入)”;
Thread t = new Thread(new Runnable() {
public void run() {
System.out.println("Hello from Runnable");
}
});
t.start();
- 适合「只做一件事、不关心结果」的场景,比如日志异步刷盘、心跳上报
- 可配合线程池使用:
ExecutorService.submit(Runnable)会忽略返回值 - 若需返回值,必须自己加共享变量 + 同步控制,容易出错
实现 Callable + Future:需要结果时的标准解法
当任务需要返回值或可能抛出异常时,Callable 是 Runnable 的增强替代。它用 call() 方法代替 run(),支持泛型返回值和受检异常。
但 Callable 不能直接交给 Thread,必须通过 ExecutorService 提交,返回 Future 对象来取结果或判断状态。
ExecutorService executor = Executors.newSingleThreadExecutor(); Futurefuture = executor.submit(() -> { Thread.sleep(1000); return "Done"; }); String result = future.get(); // 阻塞等待 executor.shutdown();
-
future.get()默认阻塞,超时版本get(long, TimeUnit)更安全 -
isDone()和isCancelled()可用于轮询状态,但轮询本身有性能开销 - 线程池关闭前务必调用
shutdown()或shutdownNow(),否则 JVM 不会退出
使用 ForkJoinPool / CompletableFuture:复杂并发任务的现代选择
对于可拆分的计算密集型任务(如归并排序、树遍历),ForkJoinPool 提供工作窃取机制,比普通线程池更高效;而 CompletableFuture 则让异步编排变得声明式、可组合。
它们不是“创建线程”的底层方式,而是更高层的抽象——你不再手动管理 Thread 实例,而是描述任务依赖与执行策略。
-
CompletableFuture.supplyAsync(...)默认使用ForkJoinPool.commonPool(),注意其线程数固定(通常为 CPU 核数 -1),IO 密集型任务应显式传入自定义线程池 -
ForkJoinPool中的子任务不要调用join()外部ForkJoinTask,否则可能引发死锁 - 过度使用
CompletableFuture.thenApplyAsync而不指定线程池,容易耗尽commonPool,拖慢整个应用
ExecutorService + Callable 或 CompletableFuture 就够了;手写 Thread 子类几乎只出现在教学或极简嵌入式场景中。真正容易被忽略的是线程池配置——大小、队列类型、拒绝策略,这些细节往往比“怎么创建”更能决定系统稳定性。










