Java中创建线程的几种方式
目录
Java 创建线程的几种方式
一、继承 Thread 类
核心原理
实现步骤
代码示例
简化写法(Lambda 表达式)
优缺点
二、实现 Runnable 接口
核心原理
实现步骤
代码示例
简化写法(Lambda 表达式)
优缺点分析
三、实现 Callable 接口
核心原理
实现步骤
代码示例
简化写法(Lambda 表达式)
优缺点分析
四、使用线程池
核心原理
线程池的 7 个核心参数
常见拒绝策略
线程池的使用步骤
代码示例
注意事项
总结对比
Java 创建线程的几种方式
在 Java 中,创建线程主要有 4 种经典方式:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口、使用线程池。本文将结合原理分析、代码示例和场景对比,系统讲解这四种方式的实现细节与差异。
一、继承 Thread 类
核心原理
Thread
类是 Java 线程的核心基类,通过继承Thread
并重写其run()
方法(定义线程执行逻辑),即可创建一个自定义线程。启动线程需调用start()
方法(而非直接调用run()
,否则会退化为普通方法调用)。
实现步骤
- 自定义类继承
Thread
; - 重写
run()
方法(线程的核心执行逻辑); - 创建自定义类实例,调用
start()
启动线程。
代码示例
public class ThreadTest1 {public static void main(String[] args) {// 创建并启动线程Thread t = new MyThread();t.start(); // 主线程逻辑for (int i = 1; i <= 5; i++) {System.out.println("主线程main输出:" + i);}}
}// 自定义线程类(继承Thread)
class MyThread extends Thread {@Overridepublic void run() {// 线程执行逻辑for (int i = 1; i <= 5; i++) {System.out.println("子线程MyThread输出:" + i);}}
}
简化写法(Lambda 表达式)
public class CreateThread1 {public static void main(String[] args) {// 使用Lambda表达式直接定义线程逻辑Thread t1 = new Thread(() -> {System.out.println("子线程输出");});t1.start();}
}
优缺点
- 优点:实现简单,直接操作线程对象;
- 缺点:Java 单继承限制(若已继承其他类则无法使用);
二、实现 Runnable 接口
核心原理
Runnable
是一个函数式接口(仅含run()
方法),通过实现该接口定义线程任务(逻辑),再将任务交给Thread
对象执行。这种方式将 “线程任务” 与 “线程对象” 解耦,更符合面向对象设计。
实现步骤
- 自定义类实现
Runnable
接口; - 重写
run()
方法(定义任务逻辑); - 创建任务实例,通过
new Thread(runnable)
绑定到线程对象; - 调用
start()
启动线程。
代码示例
public class ThreadTest2 {public static void main(String[] args) {// 创建任务对象Runnable target = new MyRunnable();// 绑定线程并启动new Thread(target).start();// 主线程逻辑for (int i = 1; i <= 5; i++) {System.out.println("主线程main输出 ===》" + i);}}
}// 自定义任务类(实现Runnable)
class MyRunnable implements Runnable {@Overridepublic void run() {// 任务执行逻辑for (int i = 1; i <= 5; i++) {System.out.println("子线程输出 ===》" + i);}}
}
简化写法(Lambda 表达式)
public class CreateThread2 {public static void main(String[] args) {// Lambda直接定义任务逻辑Thread t1 = new Thread(() -> System.out.println("子线程输出"));t1.start();System.out.println("主线程输出");}
}
优缺点分析
- 优点:避免单继承限制;任务与线程解耦,灵活性高;
- 缺点:无法直接获取线程执行结果(需结合其他机制)。
三、实现 Callable 接口
核心原理
Callable
接口(Java 1.5 引入)与Runnable
类似,但支持返回线程执行结果并抛出检查异常。需通过FutureTask
(实现了Runnable
和Future
接口)将Callable
任务封装为可执行对象,再交给Thread
执行。FutureTask
支持通过get()
方法获取线程结果(阻塞等待)。
实现步骤
- 自定义类实现
Callable
接口(指定返回值类型); - 重写
call()
方法(定义带返回值的任务逻辑); - 创建
Callable
实例,通过FutureTask
封装; - 将
FutureTask
交给Thread
启动; - 调用
FutureTask.get()
获取结果(阻塞等待线程完成)。
代码示例
// 自定义Callable任务(计算1到n的和)
class MyCallable implements Callable<String> {private int n;public MyCallable(int n) {this.n = n;}@Overridepublic String call() throws Exception {int sum = 0;for (int i = 1; i <= n; i++) {sum += i;}return "线程求出了1-" + n + "的和是:" + sum;}
}public class ThreadTest3 {public static void main(String[] args) throws Exception {// 创建Callable任务Callable<String> call1 = new MyCallable(100);Callable<String> call2 = new MyCallable(200);// 封装为FutureTaskFutureTask<String> f1 = new FutureTask<>(call1);FutureTask<String> f2 = new FutureTask<>(call2);// 启动线程new Thread(f1).start();new Thread(f2).start();// 获取结果(阻塞等待)System.out.println(f1.get()); // 输出:线程求出了1-100的和是:5050System.out.println(f2.get()); // 输出:线程求出了1-200的和是:20100}
}
简化写法(Lambda 表达式)
public class CreateThread3 {public static void main(String[] args) throws Exception {// Lambda直接定义Callable逻辑FutureTask<String> futureTask = new FutureTask<>(() -> "hello, Callable");Thread t1 = new Thread(futureTask);t1.start();// 获取线程结果System.out.println("子线程返回的结果:" + futureTask.get()); // 输出:hello, Callable}
}
优缺点分析
- 优点:支持返回值和异常抛出;适合需要异步计算结果的场景;
- 缺点:需结合
FutureTask
使用,实现略复杂;get()
方法可能阻塞主线程。
四、使用线程池
核心原理
线程池(ThreadPoolExecutor
)是 Java 并发包(java.util.concurrent
)提供的线程管理工具,通过复用线程避免频繁创建 / 销毁线程的开销,提高性能和稳定性。线程池支持统一管理线程的创建、调度和监控。
线程池的 7 个核心参数
使用ThreadPoolExecutor
构造函数时需指定以下参数:
参数 | 说明 |
---|---|
corePoolSize | 核心线程数(长期保留的活跃线程数) |
maximumPoolSize | 最大线程数(线程池允许的最大线程数) |
keepAliveTime | 非核心线程的空闲存活时间(超时后销毁) |
unit | keepAliveTime 的时间单位(如TimeUnit.SECONDS ) |
workQueue | 任务队列(存储待执行的任务,如ArrayBlockingQueue ) |
threadFactory | 线程工厂(用于创建线程,默认Executors.defaultThreadFactory() ) |
handler | 拒绝策略(任务队列和线程池均满时的处理方式) |
常见拒绝策略
AbortPolicy
(默认):直接抛出RejectedExecutionException
;DiscardPolicy
:丢弃新任务,不报错;DiscardOldestPolicy
:丢弃队列中最旧的任务,尝试重新提交新任务;CallerRunsPolicy
:由调用线程(如主线程)直接执行任务。
线程池的使用步骤
- 初始化
ThreadPoolExecutor
(指定核心参数); - 通过
submit()
或execute()
提交任务(submit()
支持返回Future
); - 任务执行完毕后,调用
shutdown()
或shutdownNow()
关闭线程池。
代码示例
import java.util.concurrent.*;public class CreateThread4 {public static void main(String[] args) throws Exception {// 初始化线程池(核心参数示例)ThreadPoolExecutor pool = new ThreadPoolExecutor(2, // 核心线程数:25, // 最大线程数:510, // 空闲存活时间:10TimeUnit.SECONDS, // 时间单位:秒new ArrayBlockingQueue<>(3), // 任务队列:容量3的阻塞队列Executors.defaultThreadFactory(), // 默认线程工厂new ThreadPoolExecutor.AbortPolicy() // 拒绝策略:直接报错);// 提交任务(支持返回Future)Future<String> future = pool.submit(() -> {System.out.println("子线程执行任务");return "任务执行完成";});// 获取任务结果(阻塞等待)System.out.println("子线程返回的结果:" + future.get()); // 输出:任务执行完成// 关闭线程池(不再接受新任务,等待现有任务完成)pool.shutdown();}
}
注意事项
- 合理配置参数:根据任务类型(CPU 密集型 / IO 密集型)调整核心线程数和队列容量;
- 正确关闭线程池:避免调用
shutdownNow()
(可能强制终止未完成任务); - 监控与调优:通过
getActiveCount()
、getQueue().size()
等方法监控线程池状态,优化参数配置。
总结对比
方式 | 核心接口 / 类 | 是否支持返回值 | 优点 | 适用场景 |
---|---|---|---|---|
继承 Thread 类 | Thread | 否 | 实现简单 | 简单任务,无继承冲突场景 |
实现 Runnable 接口 | Runnable | 否 | 解耦任务与线程,避免单继承限制 | 多线程共享任务逻辑 |
实现 Callable 接口 | Callable +FutureTask | 是 | 支持返回值和异常,适合异步计算 | 需要获取线程执行结果的场景 |
使用线程池 | ThreadPoolExecutor | 是(通过submit ) | 复用线程,降低开销,便于管理 | 高并发、长期运行的任务 |