Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java章节编写规范
  • Java基础

  • Java集合

  • Java并发

    • Java并发概述
    • 线程与进程
    • Thread类与线程生命周期
    • 线程创建与任务模型
      • 1. 继承 Thread
      • 2. 实现 Runnable
      • 3. Callable
      • 4. Future
      • 5. FutureTask
      • 6. Executor
      • 7. Runnable 与 Callable 对比
      • 8. execute 与 submit
      • 9. 任务取消
      • 10. 任务设计建议
      • 专家实践与边界
      • Tips 快问快答
    • 线程安全
    • synchronized关键字
    • volatile关键字
    • Java内存模型(JMM)
    • 线程间通信
    • 线程池
    • 并发工具类
    • 原子操作类Atomic
    • 并发锁
    • 并发容器
    • ConcurrentHashMap
    • BlockingQueue
    • CopyOnWriteArrayList
    • ThreadLocal
    • Fork/Join框架
    • ScheduledThreadPoolExecutor
    • CompletableFuture
    • 虚拟线程
    • 死锁活锁与线程问题排查
    • 并发编程最佳实践
  • Java IO

  • JVM

目录

线程创建与任务模型

Java 并发开发中要区分“线程”和“任务”。线程是执行资源,任务是要执行的逻辑。好的并发设计应把任务定义和线程管理分开。

# 1. 继承 Thread

public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("run");
    }
}

new MyThread().start();

这种方式简单,但把任务逻辑和线程对象耦合在一起,不利于复用,也不适合线程池。

# 2. 实现 Runnable

Runnable task = () -> System.out.println("run");
Thread thread = new Thread(task);
thread.start();

Runnable 表示无返回值、不能直接抛受检异常的任务。

@FunctionalInterface
public interface Runnable {
    void run();
}

优点:

  • 任务和线程分离。
  • 可以用 Lambda。
  • 可以提交给线程池。

# 3. Callable

Callable 表示有返回值、可以抛异常的任务。

Callable<Integer> task = () -> {
    return calculate();
};

接口:

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

通常与 Future、ExecutorService 配合:

ExecutorService executor = Executors.newFixedThreadPool(4);
Future<Integer> future = executor.submit(task);
Integer result = future.get();

# 4. Future

Future 表示异步任务的结果占位。

submit task
   │
   ▼
Future
   │
   ├─ get 等待结果
   ├─ cancel 取消任务
   ├─ isDone 是否完成
   └─ isCancelled 是否取消

示例:

Future<String> future = executor.submit(() -> query());
String result = future.get(1, TimeUnit.SECONDS);

get() 会阻塞当前线程,应设置超时,避免无限等待。

# 5. FutureTask

FutureTask 同时实现了 Runnable 和 Future。

FutureTask<Integer> task = new FutureTask<>(() -> 1 + 1);
new Thread(task).start();

Integer result = task.get();

它可以把 Callable 包装成可执行任务,也可以获取结果。

# 6. Executor

Executor 抽象了任务提交。

Executor executor = command -> new Thread(command).start();
executor.execute(() -> System.out.println("run"));

实际开发中更常用 ExecutorService:

ExecutorService executor = Executors.newFixedThreadPool(4);
executor.submit(task);
executor.shutdown();

任务模型:

Runnable / Callable
        │
        ▼
ExecutorService
        │
        ▼
线程池中的工作线程
        │
        ▼
执行任务

# 7. Runnable 与 Callable 对比

对比项 Runnable Callable
返回值 无 有
异常 不能直接抛受检异常 可以抛异常
方法 run() call()
常见提交方式 execute、submit submit

如果任务只是执行动作,用 Runnable;如果需要结果或异常传递,用 Callable。

# 8. execute 与 submit

方法 返回值 异常表现
execute(Runnable) 无 未捕获异常交给线程异常处理器
submit(...) Future 异常封装到 Future,调用 get 时抛出

示例:

Future<?> future = executor.submit(() -> {
    throw new RuntimeException("error");
});

future.get(); // ExecutionException

如果 submit 后从不 get,异常可能不容易被发现。

# 9. 任务取消

future.cancel(true);

参数 true 表示尝试中断正在执行任务的线程。

任务必须配合处理中断:

while (!Thread.currentThread().isInterrupted()) {
    doWork();
}

取消不是强杀,任务内部如果不响应中断,可能仍会继续执行。

# 10. 任务设计建议

  • 任务对象尽量无状态,或只持有不可变状态。
  • 不要在线程池任务中吞异常。
  • 长时间任务要支持取消和超时。
  • 不要在任务里无限创建新线程。
  • 线程池由外部管理,不要在每个任务里创建和关闭线程池。
  • IO 任务要设置超时,避免工作线程永久卡住。

# 专家实践与边界

线程创建方式本身不是重点,重点是任务生命周期如何治理。生产系统通常不应该让业务代码随意 new Thread,而是把任务提交给有边界、有命名、有监控、有拒绝策略的执行器。

业务请求
  │
  ▼
任务对象 Runnable / Callable
  │
  ▼
ExecutorService
  │
  ├─ 线程复用
  ├─ 队列缓冲
  ├─ 异常治理
  └─ 生命周期关闭
写法 适合场景 风险
new Thread 教学、极少量临时任务 无法统一治理
Runnable 无返回异步动作 异常容易丢失
Callable 有返回值任务 必须消费 Future
ExecutorService 生产任务执行 参数必须有边界
CompletableFuture 异步编排 线程池和超时要显式

设计任务时要明确:是否可取消、是否可重试、是否幂等、是否有超时、失败后由谁记录和补偿。

# Tips 快问快答

Q:继承 Thread 和实现 Runnable 哪个更推荐? A:实现 Runnable 更推荐,它让任务和线程解耦。

Q:Runnable 能返回结果吗? A:不能,需要结果时用 Callable。

Q:Callable 的异常怎么拿到? A:通过 Future.get(),异常会被包装成 ExecutionException。

Q:Future.get 有什么风险? A:会阻塞当前线程,生产代码应考虑超时。

Q:execute 和 submit 有什么区别? A:execute 无返回值,submit 返回 Future,异常表现也不同。

Q:submit 后不 get 会怎样? A:任务异常可能被封装在 Future 中而不被观察到。

Q:cancel(true) 能强制杀死线程吗? A:不能,只是尝试中断,任务需要配合响应。

Q:任务和线程为什么要分离? A:任务表达业务逻辑,线程是执行资源,分离后才能复用线程池。

上次更新: 2026/06/25, 14:19:18
Thread类与线程生命周期
线程安全

← Thread类与线程生命周期 线程安全→

Copyright © 2023-2026 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式