创建线程有几种方式?
2026年01月12日
一则或许对你有用的小广告
欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新开坑项目: 《Spring AI 项目实战(问答机器人、RAG 增强检索、联网搜索)》 正在持续爆肝中,基于
Spring AI + Spring Boot3.x + JDK 21..., 点击查看; - 《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot3.x + JDK 17..., 点击查看项目介绍; 演示链接: http://116.62.199.48:7070/; - 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/
面试考察点
面试官提出这个问题,通常旨在考察以下几个方面:
- 对 Java 线程基础知识的掌握程度:你是否清楚创建线程最核心的几种方式。
- 对不同创建方式原理的理解深度:你是否理解
Thread、Runnable、Callable之间的关系与区别,以及线程池的本质。 - 对并发编程发展趋势与最佳实践的认知:你是否了解为什么现在不推荐直接
new Thread(),而推荐使用线程池。 - 实际应用与场景选择能力:你能否根据不同的业务场景(如需要返回值、需要管理大量线程)选择合适的创建方式。
核心答案
从源码和 API 层面看,创建线程的本质方式只有一种:实例化 java.lang.Thread 对象并调用其 start() 方法。
但根据 Thread 对象执行的任务来源不同,通常将其分为 四种经典实现方式:
- 继承
Thread类,重写run()方法。 - 实现
Runnable接口,将实例作为Thread的构造参数。 - 实现
Callable接口,结合FutureTask作为Thread的构造参数。 - 通过线程池(如
ExecutorService)创建并管理线程。
此外,内部类、Lambda 表达式等方式只是上述方式的语法糖或便捷写法。
深度解析
原理/机制
Thread类本身实现了Runnable接口。方式一(继承)是直接将任务逻辑定义在Thread子类中;方式二和三是将任务逻辑(Runnable.run()或Callable.call())与线程本身(Thread)解耦,这是更推荐的做法,因为它更灵活,符合组合优于继承的原则。Callable与Runnable的核心区别在于:Callable.call()可以返回结果和抛出受检异常,而Runnable.run()不行。FutureTask实现了RunnableFuture接口,它同时包装了Callable任务并提供了对任务执行结果的异步获取(get())和取消等能力。- 线程池是生产实践中的绝对主流。它通过复用已创建的线程来避免频繁“创建-销毁”线程的巨大开销,并能提供强大的任务调度、管理和监控能力。
代码示例
// 1. 继承 Thread 类
class MyThread extends Thread {
@Override
public void run() {
System.out.println("MyThread is running.");
}
}
new MyThread().start();
// 2. 实现 Runnable 接口
Runnable task = () -> System.out.println("Runnable task is running.");
new Thread(task).start();
// 3. 实现 Callable 接口
Callable<Integer> callableTask = () -> {
Thread.sleep(1000);
return 42;
};
FutureTask<Integer> futureTask = new FutureTask<>(callableTask);
new Thread(futureTask).start();
Integer result = futureTask.get(); // 阻塞获取结果
// 4. 通过线程池创建(核心方式)
ExecutorService executor = Executors.newFixedThreadPool(4);
// 提交 Runnable 任务
executor.submit(() -> System.out.println("Task from thread pool."));
// 提交 Callable 任务
Future<Integer> future = executor.submit(callableTask);
executor.shutdown(); // 重要:使用后关闭
对比分析与最佳实践
RunnablevsCallable:需要任务返回值或处理任务执行中的受检异常时,使用Callable;否则使用Runnable。- 直接
new Thread()vs 线程池:在绝大多数生产场景中,应禁止直接创建Thread对象。原因包括:- a) 资源消耗大(创建/销毁开销);
- b) 管理困难(无法限制数量、无统一调度);
- c) 功能薄弱(缺乏执行结果处理、定期执行等功能)。
- 务必使用
ThreadPoolExecutor或其便捷工厂类(如Executors.newFixedThreadPool)来创建线程池,并根据业务场景合理配置核心参数。
常见误区
- 误以为调用
run()方法就能启动新线程。实际上,run()只是普通方法调用,start()才会让 JVM 创建新的操作系统线程并回调run()。 - 认为
Executors.newCachedThreadPool()或newSingleThreadExecutor()是万能的。前者可能导致线程数失控,后者可能堆积大量任务导致 OOM。关键场景下应直接使用ThreadPoolExecutor构造方法,明确指定核心参数(核心线程数、最大线程数、队列等)。
总结
创建线程的核心是实例化 Thread 类,但根据任务定义方式可分为继承 Thread、实现 Runnable、实现 Callable 及使用线程池四种方式;现代 Java 并发编程中,使用并正确配置线程池是唯一推荐的生产实践。