上一篇
在Java中启动线程有两种主要方式:一是继承Thread类并重写run()方法,通过调用start()启动;二是实现Runnable接口并传入Thread构造器,再调用start(),注意直接调用run()不会创建新线程。
继承Thread类
通过继承java.lang.Thread类并重写run()方法:
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程执行中: " + Thread.currentThread().getName());
}
}
// 启动线程
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // 正确启动方式(JVM调用run())
}
关键点:
- 调用
start()而非run():start()会触发JVM创建新线程并异步执行run(),直接调用run()仅是普通方法调用(仍在主线程)。 - 单继承限制:Java不支持多继承,若需继承其他类则无法使用此方式。
实现Runnable接口(推荐)
实现Runnable接口并重写run(),将实例传递给Thread对象:
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnable线程: " + Thread.currentThread().getName());
}
}
// 启动线程
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start();
}
优势:
- 解耦任务与线程:任务逻辑(Runnable)与线程管理(Thread)分离。
- 避免单继承限制:可同时实现多个接口。
- 资源复用:同一Runnable可被多个线程共享。
使用Callable和Future(带返回值)
通过Callable接口(可返回结果或抛出异常)配合线程池:
import java.util.concurrent.*;
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "Callable结果";
}
}
// 启动线程
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(new MyCallable());
System.out.println(future.get()); // 阻塞获取返回值
executor.shutdown(); // 关闭线程池
}
特点:
- 返回值支持:
Callable的call()可返回结果(Runnable无返回值)。 - 异常处理:
call()允许抛出受检异常。 - 需配合线程池使用(如
ExecutorService)。
线程池(生产环境首选)
使用ExecutorService管理线程资源:
ExecutorService executor = Executors.newFixedThreadPool(4); // 创建4线程的池
// 提交Runnable任务
executor.execute(() -> {
System.out.println("线程池任务");
});
// 提交Callable任务
Future<Integer> future = executor.submit(() -> 100);
// 关闭线程池(需显式调用)
executor.shutdown();
优势:
- 资源复用:减少线程创建/销毁开销。
- 流量控制:通过池大小限制并发数。
- 统一管理:支持任务队列、定时任务等。
Lambda表达式简化(Java 8+)
结合Lambda简化代码:
// 直接通过Thread启动
new Thread(() -> System.out.println("Lambda线程")).start();
// 线程池提交任务
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(() -> "Lambda返回值");
注意事项
- 线程安全:
- 共享数据需用
synchronized或Lock同步。 - 推荐使用
java.util.concurrent包下的原子类(如AtomicInteger)。
- 共享数据需用
- 资源释放:
- 线程池用后需调用
shutdown(),否则进程无法终止。
- 线程池用后需调用
- 避免误区:
- 不要直接调用
run():它不会启动新线程。 - 谨慎使用
stop():已废弃,强制终止可能导致资源未释放。
- 不要直接调用
- 守护线程:
- 通过
setDaemon(true)设置为守护线程(JVM退出时不等待其执行完毕)。
- 通过
| 方式 | 场景 | 优点 |
|---|---|---|
| 继承Thread | 简单临时任务 | 代码直接 |
| 实现Runnable | 需灵活性、避免单继承 | 解耦任务与线程(最常用) |
| Callable+Future | 需返回值或异常处理 | 支持结果返回 |
| 线程池 | 高并发、资源敏感型应用 | 资源复用、性能最优(生产推荐) |
实际开发中,优先选择实现Runnable接口+线程池的组合,兼顾灵活性、性能和资源管理,对于复杂异步任务,可探索CompletableFuture(Java 8+)或响应式编程框架。
引用说明:
- Oracle官方文档:Thread (Java SE 17)
- 《Java并发编程实战》(Brian Goetz等)
- Java Tutorials:Concurrency
