Java 中创建线程的四种方式详解
概述
在 Java 中,创建线程有多种方式。从 JDK 源码的角度,官方文档指出 “There are two ways to create a new thread of execution”(继承 Thread 类或实现 Runnable 接口),但从实际开发和使用角度看,通常有以下 4 种方式。
方式一:继承 Thread 类
步骤:
- 定义一个类继承
Thread类 - 重写
run()方法(方法体即线程要执行的任务) - 创建子类实例,调用
start()启动线程
示例代码:
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程执行中: " + Thread.currentThread().getName());
}
}
public class Demo {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start(); // 启动线程
}
}
缺点: Java 是单继承,如果继承了 Thread 就无法继承其他类,扩展性差。
方式二:实现 Runnable 接口
步骤:
- 定义一个类实现
Runnable接口 - 实现
run()方法 - 将实例传入
Thread构造器,调用start()启动
示例代码:
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程执行中: " + Thread.currentThread().getName());
}
}
public class Demo {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();
}
}
优点: 解耦任务逻辑与执行机制,接口方式更灵活,是推荐的基础方式。
方式三:实现 Callable 接口 + FutureTask
步骤:
- 定义一个类实现
Callable接口(泛型指定返回值类型) - 实现
call()方法(有返回值,可抛出异常) - 创建
FutureTask包装 Callable 实例 - 将 FutureTask 传入
Thread构造器,调用start()启动 - 通过
FutureTask.get()获取线程执行结果
示例代码:
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("线程执行中: " + Thread.currentThread().getName());
return 100; // 返回计算结果
}
}
public class Demo {
public static void main(String[] args) throws Exception {
FutureTask<Integer> task = new FutureTask<>(new MyCallable());
Thread t = new Thread(task);
t.start();
Integer result = task.get(); // 获取线程返回值(会阻塞等待)
System.out.println("线程返回结果: " + result);
}
}
特点:
- 支持泛型返回值
- 可以抛出受检异常
- 通过
FutureTask可以取消任务、获取结果
方式四:通过线程池创建
步骤:
- 使用
Executors工具类或直接创建ThreadPoolExecutor - 调用
execute(Runnable)或submit(Callable)提交任务 - 线程池内部通过
ThreadFactory创建线程
示例代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo {
public static void main(String[] args) {
// 创建固定大小线程池
ExecutorService pool = Executors.newFixedThreadPool(5);
// 提交任务
pool.execute(() -> {
System.out.println("线程池线程: " + Thread.currentThread().getName());
});
// 关闭线程池
pool.shutdown();
}
}
优点:
- 避免频繁创建/销毁线程的开销
- 统一管理线程生命周期
- 支持任务队列、拒绝策略等高级特性
本质分析
从本质上讲,所有创建线程的方式最终都是构造一个 Thread 类(或其子类)对象:
| 方式 | 本质 |
|---|---|
| 继承 Thread | 直接创建 Thread 子类 |
| 实现 Runnable | 创建 Thread + 传入 Runnable target |
| 实现 Callable | 创建 Thread + FutureTask(Runnable 子类) |
| 线程池 | 内部通过 DefaultThreadFactory.newThread() 创建 Thread |
面试时无论回答 1 种、2 种、4 种,只要逻辑清晰、依据充分即可。
总结对比
| 方式 | 返回值 | 异常处理 | 推荐度 |
|---|---|---|---|
| 继承 Thread | 无 | 需在 run() 内处理 | ⭐⭐ |
| 实现 Runnable | 无 | 需在 run() 内处理 | ⭐⭐⭐ |
| Callable + FutureTask | 有 | 可向外抛出 | ⭐⭐⭐⭐ |
| 线程池 | 支持 | 完整异常体系 | ⭐⭐⭐⭐⭐ |