创建线程的方式及实现
继承 Thread 类创建线程类
- 定义 Thread 类的子类,并重写该类的
run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。 - 创建 Thread 子类的实例,即创建了线程对象。
- 调用线程对象的
start()方法来启动该线程。
通过 Runnable 接口创建线程类
- 定义 Runnable 接口的实现类,并重写该接口的
run()方法,该run()方法的方法体同样是该线程的线程执行体。 - 创建 Runnable 实现类的实例,并依此实例作为 Thread 的 target 来创建 Thread 对象,该 Thread 对象才是真正的线程对象。
- 调用线程对象的
start()方法来启动该线程。
通过 Callable 和 Future 创建线程
- 创建 Callable 接口的实现类,并实现
call()方法,该call()方法将作为线程执行体,并且有返回值。 - 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的
call()方法的返回值。 - 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
- 调用 FutureTask 对象的
get()方法来获得子线程执行结束后的返回值
采用实现 Runnable、Callable 接口的方式创见多线程时:
- 优势是:线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。在这种方式下,多个线程可以共享同一个 target 对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将 CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
- 劣势是:编程稍微复杂,如果要访问当前线程,则必须使用 Thread.currentThread() 方法。
使用继承 Thread 类的方式创建多线程时:
- 优势是:编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。
- 劣势是:线程类已经继承了 Thread 类,所以不能再继承其他父类。
三种创建方式与优劣对比是标准答案,但还缺两块现代 Java 开发的必备认知:start() 与 run() 的本质区别(最高频误用),以及为什么生产环境四种方式都不用而用线程池/CompletableFuture。本文补全。
start() vs run():一号陷阱
run():普通方法调用,在当前线程同步执行,没有新线程start():请求 JVM 创建新线程(native start0),新线程的调度执行体才是 run()
Thread t = new Thread(task); |
重复 start() 抛 IllegalThreadStateException——线程是一次性资源,TERMINATED 后不可复活。
四方式全景与本质
| 方式 | 返回值 | 异常 | 本质 |
|---|---|---|---|
| 继承 Thread | 无 | 内部消化 | 任务与线程耦合(子类即线程) |
| 实现 Runnable | 无 | 内部消化 | 任务与线程解耦(target 模式) |
| Callable+FutureTask | Future.get() | call 可抛 | 解耦+结果/异常传递 |
| 线程池 submit | Future | 可捕获 | 解耦+复用+治理 |
本质演进线:**从”new 线程”到”提交任务”**——Thread/Runnable/Callable 都在回答”任务长什么样”,而”线程从哪来、活多久、多少个”应交线程池管理。
Callable/Future 的细节
- FutureTask 是 Runnable 与 Future 的桥接(它本身是 Runnable 才能塞给 Thread)
get()阻塞等待结果;get(timeout)防永久等待;cancel(true)中断执行线程- 异常语义:call 抛出的异常被封装,get() 时以 ExecutionException 重抛——不 get 就看不见异常(静默失败陷阱)
- 一个 FutureTask 只能执行一次(状态机 NEW→COMPLETING→NORMAL/EXCEPTIONAL)
生产方式:线程池 + CompletableFuture
ExecutorService pool = new ThreadPoolExecutor(...); // 显式参数,禁 Executors 工厂 |
- 线程池:复用线程(创建线程≈MB 级栈内存+内核对象,频繁创建销毁昂贵)、限并发(maxPool+队列=背压)、可监控(活跃数/队列深度)
- CompletableFuture:把 Future 的”阻塞 get”升级为”回调编排”(thenApply/thenCompose/exceptionally/allOf),多路异步聚合不再手写 CountDownLatch
选型清单
- 学习/演示:Runnable + Thread 足够
- 需要结果/异常:Callable + Future(或直接 supplyAsync)
- 任何生产并发:显式参数线程池提交任务;多路聚合用 CompletableFuture
- 定时任务:ScheduledThreadPool(而非 new Thread + sleep 循环)
- 绝不:业务代码里裸 new Thread()(无治理、无上限、异常无人知)
创建线程的方式这道题,答案的重心早已从”三种语法”移到”任务与线程分离+池化治理”:Thread/Runnable/Callable 是任务的三种形态,线程池是线程的唯一归宿,CompletableFuture 是异步编排的现代语法。按这条主线记忆,语法细节各归其位。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 非鱼小站!
评论
WalineDisqus











