在 java 并发编程中,线程是执行任务的最小单元。很多初学者只停留在 new thread().start(),但在真实项目中,这往往是最不推荐的做法。本文系统梳理 java 中创建线程的 4 种方式,对比各自的优缺点,并给出实际开发中的推荐写法。
一、继承 thread 类
这是最基础的一种方式,通过继承 thread 类并重写 run() 方法来定义线程任务。
public class mythread extends thread {
@override
public void run() {
system.out.println("thread running: " + thread.currentthread().getname());
}
public static void main(string[] args) {
mythread thread = new mythread();
thread.start();
}
}
优点:
- 写法简单直观,适合入门学习
- 可以直接调用
this.getname()获取当前线程信息
缺点:
- java 是单继承,继承 thread 后无法再继承其他类,扩展性差
- 任务逻辑与线程机制强耦合,不利于复用
- 无法返回执行结果
适用场景:
仅适合教学演示或极其简单的测试代码,生产环境不推荐使用。
二、实现 runnable 接口
将任务逻辑与线程机制解耦,是实现并发任务最常用的基础方式之一。
public class myrunnable implements runnable {
@override
public void run() {
system.out.println("runnable running: " + thread.currentthread().getname());
}
public static void main(string[] args) {
thread thread = new thread(new myrunnable());
thread.start();
}
}
优点:
- 避免单继承限制,类可以同时实现多个接口
- 任务与线程解耦,便于复用和组合
- 更符合面向对象设计原则
缺点:
run()方法没有返回值- 无法抛出受检异常
- 需要配合 thread 类使用,仍需手动管理线程生命周期
适用场景:
不需要返回结果的异步任务,是很多底层框架和工具类的基础选择。
三、实现 callable + futuretask
从 jdk 1.5 开始,引入 callable 接口,支持带返回值的线程任务。
public class mycallable implements callable<string> {
@override
public string call() throws exception {
thread.sleep(1000);
return "result from callable";
}
public static void main(string[] args) throws exception {
futuretask<string> futuretask = new futuretask<>(new mycallable());
thread thread = new thread(futuretask);
thread.start();
system.out.println("result: " + futuretask.get());
}
}
优点:
- 支持返回执行结果
- 允许抛出异常
- 可通过
futuretask获取任务状态和结果
缺点:
- 代码相对繁琐
futuretask.get()会阻塞当前线程- 仍需手动创建和管理 thread 对象
适用场景:
需要获取异步执行结果、对异常有严格要求的业务场景。
四、使用线程池(executorservice)
这是企业级开发中最推荐的方式,通过线程池统一管理和调度线程资源。
public class threadpooldemo {
public static void main(string[] args) {
executorservice executor = executors.newfixedthreadpool(3);
executor.submit(() -> {
system.out.println("task running in thread pool: " + thread.currentthread().getname());
});
executor.shutdown();
}
}
优点:
- 复用线程,降低频繁创建和销毁线程的开销
- 可控制最大并发数,防止资源耗尽
- 支持任务队列、拒绝策略、定时任务等高级特性
- 与
callable、runnable无缝集成
缺点:
- 使用门槛略高,需要理解线程池参数和运行机制
- 配置不当容易引发 oom 或任务堆积
推荐写法(真实项目):
executorservice executor = new threadpoolexecutor(
2, // 核心线程数
4, // 最大线程数
60l, // 空闲线程存活时间
timeunit.seconds,
new linkedblockingqueue<>(100), // 任务队列
executors.defaultthreadfactory(),
new threadpoolexecutor.callerrunspolicy() // 拒绝策略
);
适用场景:
几乎所有生产环境并发任务,尤其是 web 服务、后台任务、批量处理等场景。
五、四种方式对比总结
| 创建方式 | 是否有返回值 | 是否推荐 | 典型场景 |
|---|---|---|---|
| 继承 thread | 否 | ❌ 不推荐 | 学习演示 |
| 实现 runnable | 否 | ⚠️ 可用 | 简单异步任务 |
| 实现 callable | 是 | ✅ 推荐 | 需要返回结果 |
| 线程池 | 可配置 | ✅✅ 强烈推荐 | 生产环境 |
六、开发推荐写法
一句话结论:优先使用线程池,避免手动创建线程。
推荐实践:
- 使用
threadpoolexecutor自定义线程池,而不是executors快捷方法(避免 oom) - 区分 cpu 密集型和 io 密集型任务,合理设置线程数
- 配合
callable使用,便于异常处理和结果回收 - 在 spring 等框架中,优先使用
@async+ 自定义线程池
七、结语
java 线程的创建方式看似简单,但背后体现的是对并发模型、资源管理和系统设计的理解。从 thread 到线程池,不仅是语法的变化,更是工程化思维的升级。掌握这 4 种方式及其适用边界,是写出高性能、高可用 java 应用的基础。
到此这篇关于java中创建线程的4种方式优缺点对比与写法推荐的文章就介绍到这了,更多相关java创建线程内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论