【Java】多线程_创建线程的四种方式
在 Java 中,实现多线程编程是常见的开发任务。Java 提供了多种方式来创建和管理线程,满足不同场景下的需求。本文将详细介绍 Java 中创建线程的四种常见方式,包括:
- 继承
Thread
类 - 实现
Runnable
接口 - 实现
Callable
接口 +Future
获取返回值 - 使用线程池(
ExecutorService
)
1.继承Tread
类(基础)
步骤:
- 定义一个子类继承
Thread
,并重写run()
方法。 - 创建该子类的实例,调用
start()
方法启动线程。
class MyThread extends Thread {@Overridepublic void run() {System.out.println("线程执行:" + Thread.currentThread().getName());}
}public class Test {public static void main(String[] args) {Thread t = new MyThread();t.start(); // 启动线程(不是 run())}
}
原理:
Thread
类本身实现了Runnable
接口,其run()
方法是线程执行的入口。- 调用
start()
方法会触发 JVM 创建一个新的操作系统级线程,并自动调用run()
方法。
优点:简单直接,适合快速实现简单的多线程任务。
缺点:
- Java 不支持多继承,若子类已经继承了其他类,则无法再继承
Thread
。 - 线程逻辑与线程对象绑定,扩展性差。
2.实现Runnable
接口(常用)
步骤:
- 定义一个类实现
Runnable
接口,并实现run()
方法。 - 创建该类的实例,作为参数传递给
Thread
的构造函数。 - 调用
Thread
实例的start()
方法启动线程。
class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("线程执行:" + Thread.currentThread().getName());}
}public class Test {public static void main(String[] args) {Thread t = new Thread(new MyRunnable());t.start();}
}
原理:通过 new Thread(runnable)
创建新线程,本质仍是调用 Runnable
的 run()
。
优点:
- 任务逻辑(
Runnable
)与线程管理(Thread
)分离,符合单一职责原则。 - 多个线程可以共享同一个
Runnable
实例,便于资源共享。
缺点:
- 无返回值:
run()
方法无法返回计算结果。 - 异常处理受限:
run()
不能抛出受检异常(子类重写方法时,不能抛出比父类更宽的受检异常),需自行捕获处理。
3.实现 Callable
接口 + FutureTask
(有返回值)
步骤:
- 定义一个类实现
Callable<T>
接口,并实现call()
方法(可返回结果和抛出异常)。 - 用
FutureTask
包装Callable
实例。 - 将
FutureTask
作为参数传递给Thread
的构造函数,启动线程。 - 通过
FutureTask.get()
获取异步计算结果(阻塞直到任务完成)。
class MyCallable implements Callable<Integer> {@Overridepublic Integer call() {return 42; // 可以有返回值}
}public class Test {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
实现了RunnableFuture
(继承Runnable
和Future
),封装了Callable
。- 调用
start()
时,Thread
执行FutureTask.run()
,内部调用Callable.call()
并存储结果。 FutureTask.get()
用于获取线程执行结果(阻塞等待)。
优点:可以返回执行结果,支持异常处理。
缺点:
- 复杂性高:需配合
FutureTask
或线程池使用,代码复杂度增加。 - 阻塞风险:
Future.get()
会阻塞当前线程,需合理设计超时机制。
4.使用线程池(Executor
框架)
1.如何创建线程池对象:用具类-> Executors
2.获取线程池对象:Executors中的静态方法:static ExecutorService newFixedThreadPool(int nThreads) a.参数:指定线程池中最多创建的线程对象条数b.返回值ExecutorService 是线程池,用来管理线程对象3.执行线程任务: ExecutorService中的方法Future<?> submit(Runnable task) 提交一个Runnable任务用于执行 Future<T> submit(Callable<T> task) 提交一个Callable任务用于执行 4.submit方法的返回值:Future接口用于接收run方法或者call方法返回值的,但是run方法没有返回值,所以可以不用Future接收,执行call方法需要用Future接收Future中有一个方法:V get() 用于获取call方法返回值5. ExecutorService中的方法:void shutdown() 启动有序关闭,其中先前提交的任务将被执行,但不会接受任何新任务
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(2);// 提交Runnable任务
executor.execute(() -> System.out.println("线程池执行Runnable"));// 提交Callable任务并获取Future
Future<String> future = executor.submit(() -> "线程池执行Callable");
String result = future.get();
System.out.println(result);// 关闭线程池
executor.shutdown();
原理:
- 线程复用:通过维护一组核心线程(
corePoolSize
),避免频繁创建和销毁线程的开销。 - 任务队列:当线程数达到核心线程数时,新任务进入阻塞队列(如
LinkedBlockingQueue
)。 - 动态扩容:若队列满且线程数未达最大线程数(
maximumPoolSize
),创建新线程处理任务。 - 拒绝策略:当线程数达上限且队列满时,触发拒绝策略(如丢弃任务或抛出异常)。
- 任务提交:
execute(Runnable)
:提交无返回值的任务。submit(Callable)
:提交有返回值的任务,返回Future
对象。
优点:
- 复用线程,减少创建/销毁线程的开销。
- 通过队列和最大线程数限制并发量,防止资源耗尽。
- 支持任务调度、监控、统计等功能。
缺点:
- 配置复杂:需合理设置核心线程数、队列类型、拒绝策略等参数。
- 潜在风险:不关闭线程池可能导致资源泄漏(需显式调用
shutdown()
)。
5.总结对比
特性 | 继承Thread | 实现Runnable | 实现Callable | 线程池 |
---|---|---|---|---|
返回值 | ❌ 不支持 | ❌ 不支持 | ✅ 支持 | ✅ 支持 |
异常处理 | ❌ 需自行捕获 | ❌ 需自行捕获 | ✅ 可抛出受检异常 | ✅ 支持 |
资源共享 | ❌ 实例隔离 | ✅ 可共享同一实例 | ✅ 可共享同一实例 | ✅ 通过队列共享 |
任务取消 | ❌ 仅支持中断 | ❌ 仅支持中断 | ✅ 支持Future.cancel | ✅ 支持 |
扩展性 | ❌ 受单继承限制 | ✅ 高 | ✅ 高 | ✅ 极高(可配置参数) |
任务与线程关系 | 强耦合(线程即任务) | 解耦(任务独立) | 解耦(任务独立) | 完全解耦 |
线程管理方式 | 手动创建/销毁 | 手动管理 | 手动或线程池管理 | 线程池统一管理 |
资源开销 | 高(频繁创建/销毁) | 中等 | 中等 | 低(线程复用) |
线程启动方式 | JVM本地方法start0() | 包装为Thread 执行 | 包装为FutureTask 执行 | 线程池Worker循环执行 |
状态管理 | 简单生命周期状态 | 同Thread | FutureTask 状态机 | 复杂的状态控制体系 |
创建开销 | 每次new Thread高开销 | 复用Runnable实例 | 需包装FutureTask | 一次创建长期使用 |
内存泄漏风险 | 高(线程未及时回收) | 中等 | 中等 | 低(需正确关闭) |
并发控制能力 | ❌ 需手动同步 | ✅ 需同步控制 | ✅ 需同步控制 | ✅ 内置队列管理 |
OOM风险 | 高(无节制创建线程) | 中等 | 中等 | 可控(有界队列配置) |
简单独立任务 | ✅ 适合 | ✅ 适合 | ❌ 过度设计 | ❌ 资源浪费 |
资源共享任务 | ❌ 不适合 | ✅ 最佳 | ✅ 适合 | ✅ 适合 |
异步结果获取 | ❌ 不支持 | ❌ 不支持 | ✅ 必须选择 | ✅ 推荐 |
高并发请求处理 | ❌ 绝对禁止 | ❌ 不推荐 | ❌ 不推荐 | ✅ 唯一选择 |
定时/延迟任务 | ❌ 不支持 | ❌ 不支持 | ❌ 不支持 | ✅ 必须选择 |