Java 多线程实现方式(原理+实战+对比)
我会从基础实现到进阶用法,系统讲解Java多线程的所有核心实现方式,包括每种方式的原理、代码示例、适用场景和优缺点,新手也能快速上手。
一、核心实现方式(官方标准+实际常用)
Java中多线程的实现本质只有两种核心方式(Java官方定义),但实际开发中衍生出3种常用形式,先明确分类:
| 实现类型 | 核心原理 | JDK版本 | 核心特点 |
|---|---|---|---|
| 继承Thread类 | 重写run()方法,线程执行体 | 1.0+ | 简单,单继承限制 |
| 实现Runnable接口 | 重写run(),解耦任务与线程 | 1.0+ | 无单继承限制,推荐 |
| 实现Callable+FutureTask | 有返回值、可抛异常的Runnable | 1.5+ | 能获取执行结果,处理异常 |
| 线程池(Executor框架) | 复用线程,管理任务执行 | 1.5+ | 高性能,可控,生产首选 |
| 定时器(Timer/TimerTask) | 定时/周期性执行任务 | 1.3+ | 简单定时任务,单线程 |
二、方式1:继承Thread类(基础)
原理
Thread类是Java线程的核心类,继承它并重写run()方法(线程执行体),调用start()方法启动线程(JVM会调用run())。
代码示例
// 1. 继承Thread类
class MyThread extends Thread {
// 重写run():线程要执行的任务
@Override
public void run() {
for (int i = 0; i < 5; i++) {
// Thread.currentThread().getName() 获取当前线程名
System.out.println(Thread.currentThread().getName() + " 执行:" + i);
try {
Thread.sleep(100); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
// 2. 创建线程实例
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
// 3. 设置线程名(可选)
thread1.setName("线程1");
thread2.setName("线程2");
// 4. 启动线程(必须调用start(),而非直接run())
thread1.start();
thread2.start();
// 注意:直接调用run()会以主线程执行,不是多线程!
// thread1.run(); // 错误用法
}
}
输出结果(示例)
线程1 执行:0
线程2 执行:0
线程1 执行:1
线程2 执行:1
...
优缺点
- ✅ 优点:代码简单,直接操作线程对象;
- ❌ 缺点:Java单继承限制,任务与线程耦合(无法复用任务)。
三、方式2:实现Runnable接口(推荐基础方式)
原理
将“任务逻辑”与“线程对象”解耦:Runnable封装任务(重写run()),Thread负责执行任务(传入Runnable实例)。
代码示例
// 1. 实现Runnable接口,封装任务
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " 执行:" + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class RunnableDemo {
public static void main(String[] args) {
// 2. 创建任务实例
MyRunnable task = new MyRunnable();
// 3. 创建线程,传入任务
Thread thread1 = new Thread(task, "线程1");
Thread thread2 = new Thread(task, "线程2");
// 4. 启动线程
thread1.start();
thread2.start();
}
}
进阶:匿名内部类/Lambda简化(JDK8+)
public class RunnableLambdaDemo {
public static void main(String[] args) {
// Lambda表达式简化Runnable(无需定义类)
Runnable task = () -> {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " 执行:" + i);
try { Thread.sleep(100); } catch (InterruptedException e) {}
}
};
new Thread(task, "Lambda线程1").start();
new Thread(task, "Lambda线程2").start();
}
}
优缺点
- ✅ 优点:无单继承限制,任务与线程解耦(可复用任务);
- ❌ 缺点:run()无返回值,无法抛出检查异常(只能捕获)。
四、方式3:实现Callable+FutureTask(有返回值)
原理
Callable是Runnable的增强版:
call()方法有返回值(泛型);call()可抛出检查异常;- FutureTask封装Callable,获取执行结果(get())。
代码示例
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
// 1. 实现Callable接口,指定返回值类型
class MyCallable implements Callable<Integer> {
private int num;
public MyCallable(int num) {
this.num = num;
}
// 有返回值、可抛异常的任务
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= num; i++) {
sum += i;
System.out.println(Thread.currentThread().getName() + " 计算:" + i);
Thread.sleep(100);
}
return sum; // 返回计算结果
}
}
public class CallableDemo {
public static void main(String[] args) {
// 2. 创建Callable任务
Callable<Integer> callable = new MyCallable(5);
// 3. FutureTask封装Callable(既是Runnable,又是Future)
FutureTask<Integer> futureTask = new FutureTask<>(callable);
// 4. 启动线程
Thread thread = new Thread(futureTask, "计算线程");
thread.start();
// 5. 获取任务结果(get()会阻塞,直到任务完成)
try {
Integer result = futureTask.get();
System.out.println("计算结果:" + result); // 输出15
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
核心方法说明
futureTask.get():阻塞获取返回值;futureTask.cancel(boolean mayInterruptIfRunning):取消任务;futureTask.isDone():判断任务是否完成。
优缺点
- ✅ 优点:有返回值、可处理异常;
- ❌ 缺点:get()方法会阻塞,需合理处理超时。
五、方式4:线程池(Executor框架)(生产首选)
原理
线程池复用线程(避免频繁创建/销毁线程的开销),管理任务队列,控制并发数,是生产环境的最优选择。
核心线程池类型(JDK提供)
| 线程池类型 | 核心特点 | 适用场景 |
|---|---|---|
| FixedThreadPool | 固定核心线程数,无最大线程数 | 稳定的并发任务 |
| CachedThreadPool | 核心线程0,最大线程无限 | 短期、高频小任务 |
| SingleThreadExecutor | 单线程执行 | 顺序执行任务 |
| ScheduledThreadPool | 定时/周期性执行 | 定时任务、延迟任务 |
代码示例:固定线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo {
public static void main(String[] args) {
// 1. 创建固定线程池(核心线程数=2)
ExecutorService executor = Executors.newFixedThreadPool(2);
// 2. 提交任务(Runnable)
for (int i = 0; i < 5; i++) {
int taskNum = i;
executor.submit(() -> {
System.out.println(Thread.currentThread().getName() + " 执行任务" + taskNum);
try { Thread.sleep(200); } catch (InterruptedException e) {}
});
}
// 3. 提交Callable任务(有返回值)
executor.submit(() -> {
int sum = 0;
for (int i = 1; i <= 3; i++) sum += i;
System.out.println("Callable任务结果:" + sum);
return sum;
});
// 4. 关闭线程池(生产中建议优雅关闭)
executor.shutdown(); // 等待所有任务完成后关闭
// executor.shutdownNow(); // 立即关闭,中断正在执行的任务
}
}
进阶:自定义线程池(推荐生产使用)
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CustomThreadPoolDemo {
public static void main(String[] args) {
// 自定义线程池参数(核心池大小、最大池大小、空闲时间、队列、拒绝策略)
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
4, // 最大线程数
60, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new ArrayBlockingQueue<>(2), // 任务队列(容量2)
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略(主线程执行)
);
// 提交任务
for (int i = 0; i < 7; i++) {
int taskNum = i;
executor.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 执行任务" + taskNum);
try { Thread.sleep(100); } catch (InterruptedException e) {}
});
}
executor.shutdown();
}
}
优缺点
- ✅ 优点:复用线程、控制并发、管理任务、高性能;
- ❌ 缺点:需合理配置参数(核心数、队列、拒绝策略)。
六、方式5:定时器(Timer/TimerTask)(定时任务)
原理
Timer是Java早期的定时任务工具,单线程执行TimerTask(实现Runnable),支持延迟/周期性执行。
代码示例
import java.util.Timer;
import java.util.TimerTask;
public class TimerDemo {
public static void main(String[] args) {
Timer timer = new Timer("定时任务线程");
// 延迟1秒执行,每2秒重复执行
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 执行定时任务:" + System.currentTimeMillis());
}
}, 1000, 2000);
// 5秒后取消定时器
try {
Thread.sleep(5000);
timer.cancel();
System.out.println("定时器已取消");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
注意事项
- ❌ Timer是单线程,一个任务异常会导致所有任务终止;
- ✅ 生产中推荐用
ScheduledThreadPoolExecutor替代Timer。
七、核心对比与选型建议
| 实现方式 | 返回值 | 异常处理 | 单继承限制 | 性能 | 适用场景 |
|---|---|---|---|---|---|
| 继承Thread | 无 | 只能捕获 | 有 | 一般 | 简单demo,不推荐生产 |
| 实现Runnable | 无 | 只能捕获 | 无 | 较好 | 无返回值的普通任务 |
| Callable+FutureTask | 有 | 可抛出 | 无 | 较好 | 需返回值/处理异常的任务 |
| 线程池 | 可选 | 可处理 | 无 | 最优 | 生产环境所有场景(推荐) |
| Timer/TimerTask | 无 | 只能捕获 | 无 | 较差 | 简单定时任务(替代:ScheduledThreadPool) |
选型核心原则
- 生产环境优先用线程池(性能、可控性最优);
- 需返回值用
Callable + 线程池; - 简单demo可用Runnable/Thread;
- 定时任务用
ScheduledThreadPoolExecutor,而非Timer。
总结
- Java多线程核心实现是「继承Thread」和「实现Runnable」,其余是衍生增强;
- 「Callable+FutureTask」解决返回值/异常问题,「线程池」解决性能/管理问题;
- 生产环境首选线程池(自定义参数更可控),避免直接创建Thread;
- 核心区别:返回值、异常处理、线程复用,选型需结合业务场景。
百流积聚,江河是也;文若化风,可以砾石。

浙公网安备 33010602011771号