创建线程的方式#
一句话答案#
创建线程本质只有”继承 Thread”和”实现 Runnable/Callable 再交给 Thread 或线程池执行”两类,生产中应通过线程池统一管理;
start()才会真正起新线程,直接调run()只是普通方法调用。
核心要点
四种常见写法:
1. 继承 Thread,重写 run()
class MyThread extends Thread {
public void run() { System.out.println("run"); }
}
new MyThread().start();java- 缺点:Java 单继承,继承了 Thread 就不能再继承别的类,灵活性差
2. 实现 Runnable 接口(推荐)
Runnable task = () -> System.out.println("run");
new Thread(task).start();java- 优点:任务与线程解耦,可复用、可交给线程池;类还能继承别的父类
- 本质:Thread 也是实现了 Runnable;这是组合优于继承的体现
3. 实现 Callable + FutureTask(有返回值/能抛异常)
Callable<Integer> c = () -> 42;
FutureTask<Integer> ft = new FutureTask<>(c);
new Thread(ft).start();
Integer result = ft.get(); // 阻塞获取返回值java- 与 Runnable 区别:有返回值、可抛受检异常,详见Callable与Future
4. 线程池(生产首选)
ExecutorService pool = Executors.newFixedThreadPool(4);
pool.submit(() -> doWork());java- 复用线程、控制并发数、统一管理生命周期,详见线程池核心参数与执行流程
严格说只有一种本质方式:
- 不管哪种写法,最终都是把任务逻辑(run 方法体)传给
Thread去执行 - 继承 Thread 是重写 run,实现 Runnable 是把 run 委托给传入的对象——底层都是一个 Thread + 一个 run
start() vs run()(高频送命题):
start() | run() | |
|---|---|---|
| 行为 | 启动新线程,由 JVM 调度后回调 run | 当前线程直接执行 run,不开新线程 |
| 线程数 | 多一个线程 | 还是当前线程(串行) |
| 重复调用 | 同一线程 start 两次抛 IllegalThreadStateException | 可多次调用(就是普通方法) |
Executors 几种内置线程池(常被追问):
newFixedThreadPool/newSingleThreadExecutor:用无界 LinkedBlockingQueue,任务堆积可能 OOMnewCachedThreadPool:最大线程数Integer.MAX_VALUE,线程暴涨可能 OOM- 阿里规约:不推荐用 Executors,应手动
new ThreadPoolExecutor明确队列容量和拒绝策略
面试回答(2分钟版)
创建线程通常说四种方式:继承 Thread 重写 run、实现 Runnable 接口、实现 Callable 配合 FutureTask、以及用线程池。但严格来讲本质只有一种,因为不管哪种写法最后都是把任务逻辑交给一个 Thread 对象去执行,继承 Thread 是直接重写它的 run,实现 Runnable 是把 run 委托给传进去的对象,所以更推荐 Runnable,它把任务和线程解耦、能复用、还能交给线程池,而且避免了 Java 单继承的限制。Callable 和 Runnable 的区别是它有返回值、能抛受检异常,要配合 FutureTask 用 get 拿结果。生产环境一定是用线程池,能复用线程、控制并发数。还有个经典点是 start 和 run 的区别:调 start 才会真正启动一个新线程由 JVM 调度去回调 run,而直接调 run 只是在当前线程里普通地执行一遍方法体,根本没开新线程;另外同一个线程 start 两次会抛 IllegalThreadStateException。最后线程池不建议用 Executors 的工厂方法,因为 FixedThreadPool 用无界队列、CachedThreadPool 线程数无上限,都有 OOM 风险,应该手动 new ThreadPoolExecutor。
追问与易错
追问方向:
- “直接调用 run() 会怎样?”→ 在当前线程串行执行方法体,不会启动新线程
- “为什么推荐 Runnable 而不是继承 Thread?”→ 避免单继承限制,任务与线程解耦可复用、可入线程池
- “Runnable 和 Callable 的区别?”→ Callable 有返回值、可抛受检异常,配合 Future 获取结果
- “一个线程能 start 两次吗?”→ 不能,第二次抛 IllegalThreadStateException
- “为什么不推荐 Executors 创建线程池?”→ Fixed/Single 用无界队列、Cached 线程数无上限,都可能 OOM
易错点:
- ❌ “线程池是第几种创建线程的全新方式”——它本质还是封装了 Thread + Runnable,只是管理方式
- ❌ “调用 run() 也能并发”——run() 只是普通方法调用,纯串行
- ❌ “实现 Runnable 就比继承 Thread 性能高”——区别在设计灵活性,不是性能