基础使用
创建线程
- 继承 Thread 类,重写 run()
代码块收起展开
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程执行: " + Thread.currentThread().getName());
}
}
// 使用
public class Main {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start(); // 注意!是 start() 不是 run()
}
}关键坑:调 start() 才会开新线程;直接调 run()
只是普通方法调用,还在主线程里跑,不会开新线程。
缺点:Java 单继承,继承了 Thread 就不能再继承别的类,太占用继承名额。
- 实现 Runnable 接口(最推荐的传统写法)
代码块收起展开
class MyTask implements Runnable {
@Override
public void run() {
System.out.println("任务执行: " + Thread.currentThread().getName());
}
}
// 使用
public class Main {
public static void main(String[] args) {
MyTask task = new MyTask();
Thread t = new Thread(task); // 把任务交给 Thread
t.start();
}
}为什么比继承 Thread 好:
- 只是实现接口,不占继承名额,类还能继承别的
- 任务和线程分离 —— 任务(Runable)负责”干什么”,线程(Thread)负责”怎么跑”,职责清晰
- 同一个 Runnable 可以交给多个线程,或丢给线程池
Lambda 简写
代码块收起展开
Thread t = new Thread(() -> {
System.out.println("Lambda 任务");
});
t.start();- 实现 Callable + FutureTask(能拿返回值 / 抛异常)
前两种的 run() 返回 void,拿不到结果也不能抛检查异常。Callable 补上这个缺口。
代码块收起展开
import java.util.concurrent.*;
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception { // 有返回值,还能抛异常
Thread.sleep(1000);
return 42;
}
}
// 使用
public class Main {
public static void main(String[] args) throws Exception {
// FutureTask 包装 Callable,它本身是 Runnable,能塞给 Thread
FutureTask<Integer> futureTask = new FutureTask<>(new MyCallable());
Thread t = new Thread(futureTask);
t.start();
Integer result = futureTask.get(); // 阻塞等待结果
System.out.println("结果: " + result); // 42
}
}对照记忆:
Runnable → run() → 返回 void,不能抛检查异常
Callable → call() → 有返回值,能抛异常
- 线程池(实际开发唯一推荐)
前面三种都是手动 new Thread,实际开发几乎不这么干 ——
频繁创建销毁线程开销大,还可能无限创建撑爆内存。生产环境统一用线程池。
代码块收起展开
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) {
// 阿里规范:手动 new,不用 Executors 的快捷方法(防无界队列 OOM)
ExecutorService pool = new ThreadPoolExecutor(
2, // 核心线程数
4, // 最大线程数
60L, TimeUnit.SECONDS, // 空闲线程存活时间
new ArrayBlockingQueue<>(10) // 有界任务队列
);
// 提交 Runnable(无返回值)
pool.execute(() -> System.out.println("execute 任务"));
// 提交 Callable(有返回值)
Future<Integer> future = pool.submit(() -> 100);
try {
System.out.println("结果: " + future.get());
} catch (Exception e) {
e.printStackTrace();
} finally {
pool.shutdown(); // 用完记得关
}
}
}“为什么不用 Executors.newFixedThreadPool()”,
答:Executors 的快捷方法底层用无界队列(LinkedBlockingQueue,容量
Integer.MAX_VALUE),任务堆积会 OOM;newCachedThreadPool 最大线程数是
▎ Integer.MAX_VALUE,可能创建海量线程。所以《阿里巴 Java 开发手册》强制要求手动 new
▎ ThreadPoolExecutor,参数自己控制。
- 现代补充:CompletableFuture / 虚拟线程
不是”创建线程的基础方式”,但现代 Java 常用,提一嘴显得跟得上版本。
代码块收起展开
CompletableFuture(异步编排):
CompletableFuture.supplyAsync(() -> 42)
.thenApply(x -> x * 2)
.thenAccept(System.out::println); // 输出 84
虚拟线程(JDK21 正式发布,轻量级线程,能开百万个):
// JDK21+
Thread.startVirtualThread(() -> {
System.out.println("虚拟线程");
});
// 或用虚拟线程池
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> System.out.println("虚拟线程任务"));
}