Java 中如何创建多线程?
- 标签:后端、Java、Java并发
🏛 业务架构师
四种创建方式我按业务可用性分层:
- 继承 Thread / 实现 Runnable:基础语法。
new Thread().start()在业务代码里是违规操作——线程无上限创建,高峰期 OOM 的经典来源; - 实现 Callable + FutureTask:需要返回值时用,但单个 Future 阻塞 get 仍是串行思维;
- 线程池(ExecutorService):业务代码的唯一正确入口。资源受控(上限、队列)、可复用、可观测、可调参。
我的规约很明确:生产代码禁止裸 new Thread,一切任务提交给线程池,并且:
- 池子按业务隔离(订单池、通知池分开)——混用池的后果是通知任务把队列塞满,订单任务被拒绝,一个非核心业务拖死核心业务;
- 每个池有名字、有拒绝策略、有监控:拒绝策略按业务定(交易任务 Abort 快速失败+告警;日志任务 Discard 保主链路);
- 提交任务带超时与取消语义,别让池里堆满永不完成的僵尸任务。
再上一层是异步编排:CompletableFuture 把"并行调用三个下游再聚合"(比价、详情页聚合)从回调地狱变成声明式管道,配自定义线程池使用——并行调用三个各 200ms 的服务,总耗时从 600ms 变 200ms,直接写进接口 P99 的优化。
一句话:创建线程的语法题背后是资源治理题——线程是稀缺资源,入口收口到池、池按业务分、行为可观测,这三条做到了,并发才算安全。
🏗 系统架构师
Java 创建线程的几种方式,我按演进 + 各自适用讲:
- 继承 Thread / 实现 Runnable:基础形态,Runnable 更优(不占继承位、任务与执行解耦),但无返回值、无异常传播;
- 实现 Callable + FutureTask:有返回值、可抛受检异常,get() 阻塞等待结果;
- 线程池提交(我的默认姿势):
executor.submit(callable)返回 Future,或 CompletableFuture.supplyAsync(...).thenCombine(...) 组合异步流水线——生产代码里我禁止直接 new Thread:裸线程无法治理(无名字、无监控、无上限),高并发下线程爆炸; - 线程工厂定制:Guava ThreadFactoryBuilder 或自建 Factory 命名线程(app-biz-pool-1),排查线程堆栈时救命;
- 虚拟线程(JDK 21,补充加分):
Thread.ofVirtual()或 Executors.newVirtualThreadPerTaskExecutor(),百万级轻量线程,阻塞成本极低,IO 密集服务可直接按请求一线程编程,告别异步回调地狱——但要配池化的第三方资源(DB 连接)限流。
工程规范:线程统一由线程池管理(命名、有界队列、拒绝策略、监控埋点),这是线上可运维性的底线。
💻 全栈开发专家
四种方式,工程上我按序号从后往前选:
java
// 1. 继承 Thread
new Thread(() -> log.info("run")).start();
// 2. 实现 Runnable(无返回值)
new Thread(new RunnableImpl(), "worker-1").start();
// 3. Callable + FutureTask(有返回值/异常)
FutureTask<Integer> ft = new FutureTask<>(() -> calc());
new Thread(ft).start();
Integer r = ft.get(); // 阻塞取结果
// 4. 线程池(生产唯一正确答案)
ThreadPoolExecutor pool = new ThreadPoolExecutor(
8, 16, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000),
new ThreadFactoryBuilder().setNameFormat("biz-%d").build(), // 名字必配
new ThreadPoolExecutor.CallerRunsPolicy());
pool.submit(() -> doWork());规范与理由:
- 禁用
Executors.newFixedThreadPool/newCachedThreadPool(阿里规约):Fixed 队列无界会 OOM,Cached 最大线程 Integer.MAX_VALUE 会打爆系统;必须手动ThreadPoolExecutor,参数可评估、可监控。 - 线程池要全局统一管理(命名、监控上报),散落各处的匿名线程池是故障排查黑洞。
- JDK 21 的虚拟线程(
Thread.ofVirtual())适合高阻塞 IO 场景,一行动代码十万并发,但 synchronized 块内阻塞会 pin 住载体线程(Loom 初期),替换前要压测。
衍生:定时任务用 ScheduledThreadPoolExecutor;并行聚合用 CompletableFuture.supplyAsync(..., pool)。