Java中封装线程任务的核心是解耦“做什么”与“由谁执行”:Runnable适用于无返回值任务,Callable支持返回值与异常,继承Thread不推荐因违反单一职责;均需遵循封装原则保障线程安全。

在 Java 面向对象实践中,将线程任务封装为可执行对象,核心是**把“要做什么”从“由谁来执行”中解耦出来**,体现封装思想——任务逻辑独立成类,执行机制交由线程系统统一调度。
用 Runnable 接口封装任务逻辑
这是最轻量、最常用的方式。Runnable 是一个函数式接口,只定义了一个无参无返回值的 run() 方法。
- 定义一个类实现 Runnable 接口,重写 run(),把具体业务逻辑写在里面
- 该类不依赖 Thread,职责单一:只描述“做什么”,不关心“怎么启动”
- 创建该类实例后,可传给 Thread 构造器(
new Thread(task)),也可交给线程池(executor.submit(task)) - 适合无需返回结果、也不需抛出受检异常的任务
用 Callable 接口封装带结果的任务
当任务需要返回值或可能抛出异常时,Runnable 就不够用了。Callable 提供了 call() 方法,支持泛型返回值和 throws 声明。
- 实现 Callable
接口,重写 call(),返回指定类型结果(如 String、Integer) - 不能直接交给 Thread,必须用 FutureTask 包装(它同时实现了 Runnable 和 Future)
- 包装后既可传给 Thread 启动,又能通过
future.get()获取结果、捕获异常 - 更符合面向对象的“职责分离”:Callable 定义任务+结果,FutureTask 提供执行契约与结果获取能力
通过继承 Thread 类封装(不推荐但需理解)
虽然技术上可行,但违背面向对象的设计原则。
- 子类继承 Thread 后重写 run(),把任务逻辑写在其中
- 问题在于:Java 不支持多继承,一旦继承了 Thread,就无法再继承其他业务类,扩展性差
- Thread 类本身已承担“线程控制”职责,再塞进业务逻辑,违反单一职责原则
- 实际项目中几乎不用,仅用于教学演示或极简场景
结合封装特性让任务更安全可控
无论用 Runnable 还是 Callable,都应遵循面向对象的封装规范:
- 任务类中的共享数据(如计数器、缓存容器)应设为 private,并提供线程安全的访问方式(如加锁、使用并发集合)
- 避免在任务类中直接操作外部对象的公有字段;推荐通过构造方法传入不可变参数或线程安全对象引用
- 若任务依赖外部服务(如数据库连接池),应通过构造注入,而非在 run/call 中硬编码创建,便于单元测试和替换
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











