线程就像公司里的工人,负责执行具体的任务。Java 中创建线程的三种方式,本质是 让工人知道要干什么活。以下用最直白的方式解释:
1. 继承 Thread
类:直接当工人
步骤:
- 写一个类继承
Thread
- 重写
run()
方法(定义任务内容) - 调用
start()
启动线程
代码示例:
class MyThread extends Thread {
@Override
public void run() {
System.out.println("我是继承Thread的线程!");
}
}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // 启动线程
}
}
缺点:
- 单继承限制:Java 只能单继承,如果类已经继承了其他类,就不能用这种方式了。
- 代码耦合:任务和线程绑定,不够灵活。
2. 实现 Runnable
接口:让工人拿任务单
步骤:
- 写一个类实现
Runnable
- 实现
run()
方法(定义任务内容) - 把任务交给
Thread
执行
代码示例:
class MyTask implements Runnable {
@Override
public void run() {
System.out.println("我是实现Runnable的线程!");
}
}
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(new MyTask());
thread.start();
}
}
优点:
- 解耦:任务(
Runnable
)和线程(Thread
)分离,更灵活。 - 支持多实现:可以同时实现多个接口。
3. 实现 Callable
接口:要汇报结果的工人
特点:
- 和
Runnable
类似,但任务可以有返回值,还能抛异常。 - 需要配合
FutureTask
或线程池使用。
代码示例:
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class MyCallableTask implements Callable<String> {
@Override
public String call() throws Exception {
return "我是Callable线程,我有返回值!";
}
}
public class Main {
public static void main(String[] args) throws Exception {
FutureTask<String> futureTask = new FutureTask<>(new MyCallableTask());
Thread thread = new Thread(futureTask);
thread.start();
System.out.println(futureTask.get()); // 获取返回值
}
}
优点:
- 能返回结果:适合需要获取任务执行结果的场景。
- 异常处理:任务中可以抛出异常,由主线程捕获。
三种方式对比
方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
继承 Thread | 简单直接 | 单继承限制,耦合高 | 简单测试、临时任务 |
实现 Runnable | 灵活解耦,可复用 | 无返回值 | 大多数多线程场景 |
实现 Callable | 支持返回值和异常 | 需配合 FutureTask 使用 | 需要结果或异常处理的场景 |
实际开发建议
- 优先用
Runnable
:灵活、解耦,适合大部分场景。 - 需要返回值时用
Callable
:比如异步计算、批量处理任务。 - 别直接继承
Thread
:除非需要重写Thread
的其他方法(但这种情况很少)。
附:线程池才是终极答案!
实际项目中,一般不会直接 new Thread()
,而是用 线程池 管理线程,避免频繁创建销毁线程的开销。
// 使用线程池执行 Runnable
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.execute(new MyTask());
总结口诀:
「创建线程三方式,继承Thread最简单
Runnable更灵活,Callable能返结果
实际开发用池管,性能稳定不翻车!」