Java 并发编程】线程创建 6 种方式:Thread/Runnable/Callable 核心类解析+线程池使用说明

絕刀狂花
发布: 2025-11-27 17:40:02
原创
457人浏览过

## 引言:线程与java并发的核心在java中,线程是实现并发编程的基础单元,它允许程序在同一时间执行多个任务(如后台处理、异步通信等)。java提供了多种创建线程的方式,每种方式都有其设计初衷、适用场景和优缺点。本文将以**总分总**结构,详细拆解java中创建线程的6种核心方式,包括原理剖析、代码实战、注意事项,并通过流程图辅助理解,帮助你彻底掌握线程创建的底层逻辑与实践技巧。

## 一、继承Thread类(最基础的线程创建方式)`Thread`是Java中封装线程操作的核心类,它本身实现了`Runnable`接口。通过**继承Thread类并重写run()方法**,可以定义线程的执行逻辑,这是最基础的线程创建方式。

### 1. 原理剖析+ `Thread`类的核心作用:封装了线程的生命周期(新建、就绪、运行、阻塞、终止)和底层操作系统调用(如启动线程、中断线程)。+ 线程执行逻辑的载体:`run()`方法是线程的“任务入口”,当线程启动后,JVM会自动调用该方法执行任务;若未重写`run()`,则会执行父类`Thread`的默认实现(无实际逻辑)。

### 2. 实现步骤1. 定义自定义类,继承`Thread`类;2. 重写`Thread`类的`run()`方法,在方法体内编写线程要执行的任务逻辑;3. 创建自定义类的实例(即线程对象);4. 调用线程对象的`start()`方法,启动线程(**注意:不可直接调用run()方法**)。

### 3. 完整代码示例```java/** * 方式1:继承Thread类创建线程 */public class ThreadExtendDemo extends Thread { // 1. 重写run()方法,定义线程任务 @Override public void run() { // 线程要执行的逻辑:这里模拟循环打印 for (int i = 0; i \u003c 5; i++) { // Thread.currentThread().getName():获取当前线程名称 System.out.println(\"[\" + Thread.currentThread().getName() + \"] 执行任务,i=\" + i); try { // 模拟任务耗时:让线程休眠100ms,释放CPU资源 Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }

立即学习Java免费学习笔记(深入)”;

public static void main(String[] args) { // 2. 创建线程实例(自定义Thread子类对象) ThreadExtendDemo thread1 = new ThreadExtendDemo(); ThreadExtendDemo thread2 = new ThreadExtendDemo(); // 可选:设置线程名称(便于调试) thread1.setName(\"线程A\"); thread2.setName(\"线程B\"); // 3. 调用start()方法启动线程(JVM会自动调用run()) thread1.start(); thread2.start(); // 主线程逻辑:与子线程并发执行 System.out.println(\"[\" + Thread.currentThread().getName() + \"] 主线程执行完毕\"); }}```

**执行结果(部分)**:

神采PromeAI
神采PromeAI

将涂鸦和照片转化为插画,将线稿转化为完整的上色稿。

神采PromeAI 97
查看详情 神采PromeAI

```plain[main] 主线程执行完毕[线程A] 执行任务,i=0[线程B] 执行任务,i=0[线程A] 执行任务,i=1[线程B] 执行任务,i=1

```

注意:输出顺序是由每个线程自己抢的,不是固定的。

### 4. 关键注意点:`start()` vs `run()`很多初学者会直接调用`run()`方法,但这是错误的!二者的核心区别在于是否创建新线程:

```latexgraph TD A[调用 thread.start() ] --\u003e B[JVM向操作系统申请新线程资源] B --\u003e C[新线程启动后,自动调用 run() 方法] C --\u003e D[任务在新线程中执行] E[直接调用 thread.run() ] --\u003e F[无新线程创建,run() 作为普通方法执行]graph TD A[调用 thread.start() ] --\u003e B[JVM向操作系统申请新线程资源] B --\u003e C[新线程启动后,自动调用 run() 方法] C --\u003e D[任务在新线程中执行] E[直接调用 thread.run() ] --\u003e F[无新线程创建,run() 作为普通方法执行]```

**示例验证**:若将上述代码中的`thread1.start()`改为`thread1.run()`,执行结果会变成“主线程先执行完run()逻辑,再执行主线程打印”,完全失去并发效果。

### 5. 优缺点分析| 优点 | 缺点 || --- | --- || 实现简单,直接继承Thread即可 | 受Java单继承限制:若类已继承其他类(如Object外的类),则无法再继承Thread || 可直接通过`this`获取当前线程对象 | 任务与线程耦合:线程对象与任务逻辑绑定,无法复用线程执行不同任务 |

## 二、实现Runnable接口(解耦首选方式)为解决`Thread`类的单继承限制,Java提供了`Runnable`接口——它仅定义了一个`run()`方法,代表线程要执行的任务。通过**实现Runnable接口**,可以将“线程对象”与“任务逻辑”解耦,是实际开发中更常用的方式。

### 1. 原理剖析+ `Runnable`是一个函数式接口(Java 8+),定义如下:

```java@FunctionalInterfacepublic interface Runnable { void run(); // 仅包含任务逻辑,无返回值、不抛checked异常}```

+ 核心逻辑:`Thread`类有一个构造器`Thread(Runnable target)`,可接收`Runnable`实例(任务)。当线程启动后,JVM会调用`target.run()`,从而实现“线程对象”与“任务”的分离。

### 2. 实现步骤1. 定义自定义类,实现`Runnable`接口;2. 重写`Runnable`的`run()`方法,编写任务逻辑;3. 创建`Runnable`实例(任务对象);4. 创建`Thread`实例,将`Runnable`实例传入`Thread`构造器;5. 调用`Thread`实例的`start()`方法启动线程。

### 3. 完整代码示例```java/** * 方式2:实现Runnable接口创建线程 */public class RunnableImplDemo implements Runnable { // 1. 重写run()方法,定义任务逻辑 @Override public void run() { for (int i = 0; i \u003c 5; i++) { System.out.println(\"[\" + Thread.currentThread().getName() + \"] 执行任务,i=\" + i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }

public static void main(String[] args) { // 2. 创建任务对象(Runnable实例) RunnableImplDemo task = new RunnableImplDemo(); // 3. 创建线程对象,将任务传入Thread构造器(关键:线程与任务分离) Thread thread1 = new Thread(task, \"线程C\"); // 第二个参数直接设置线程名 Thread thread2 = new Thread(task, \"线程D\"); // 4. 启动线程 thread1.start(); thread2.start(); System.out.println(\"[\" + Thread.currentThread().getName() + \"] 主线程执行完毕\"); }}```

**执行结果(部分)**:

```plain[main] 主线程执行完毕[线程C] 执行任务,i=0[线程D] 执行任务,i=0[线程C] 执行任务,i=1[线程D] 执行任务,i=1```

### 4. 核心优势:任务复用与解耦与“继承Thread”相比,`Runnable`的核心优势是**任务可复用**——同一个`Runnable`实例(任务)可以被多个`Thread`实例(线程)共享执行。

例如,若要实现“两个线程共同累加一个计数器”,用`Runnable`可轻松实现(任务共享计数器):

```javapublic class SharedTaskDemo implements Runnable { private int count = 0; // 共享计数器(线程安全需额外处理,此处仅演示复用)

@Override public void run() { for (int i = 0; i \u003c 3; i++) { count++; System.out.println(\"[\" + Thread.currentThread().getName() + \"] count=\" + count); } }

public static void main(String[] args) { SharedTaskDemo sharedTask = new SharedTaskDemo(); // 两个线程共享同一个任务对象,操作同一个count new Thread(sharedTask, \"线程E\").start(); new Thread(sharedTask, \"线程F\").start(); }}```

**执行结果(可能)**:

```plain[线程E] count=1[线程F] count=2[线程E] count=3[线程F] count=4[线程E] count=5[线程F] count=6```

### 5. 与Thread类的对比| 对比维度 | 继承Thread类 | 实现Runnable接口 || --- | --- | --- || 继承限制 | 受单继承限制,无法再继承其他类 | 无继承限制,可同时实现其他接口 || 耦合度 | 线程与任务耦合(线程对象即任务) | 线程与任务解耦(任务独立,可复用) || 代码扩展性 | 差(任务逻辑无法单独抽离) | 好(任务可作为参数传递,便于模块化) || Java 8支持 | 可通过匿名内部类简化,但不如Runnable灵活 | 支持Lambda表达式(因Runnable是函数式接口) |

### 6. 优缺点分析| 优点 | 缺点 || --- | --- || 无单继承限制,灵活性更高 | 无法直接获取线程对象:需通过`Thread.currentThread()`获取,而非`this` || 任务与线程解耦,支持任务复用 | 无返回值:`run()`方法无返回值,无法获取线程执行结果 || 支持Lambda表达式(Java 8+),代码更简洁 | 不抛checked异常:`run()`方法声明无异常抛出,需在方法内部捕获 |

## 三、实现Callable接口(带返回值的线程)无论是`Thread`还是`Runnable`,都存在一个明显缺陷:**无法获取线程执行的返回结果**。为解决这个问题,Java 5引入了`Callable`接口——它与`Runnable`类似,但支持返回值和抛出checked异常。

不过,`Callable`不能直接传入`Thread`(因`Thread`仅接收`Runnable`),需借助`FutureTask`作为“桥梁”(`FutureTask`实现了`Runnable`接口)。

### 1. 原理剖析#### (1)Callable接口定义`Callable`是一个泛型接口,泛型参数代表返回值类型,核心方法为`call()`:

```java@FunctionalInterfacepublic interface Callable\u003cV\u003e { V call() throws Exception; // 有返回值、可抛checked异常}```

#### (2)FutureTask的桥梁作用`FutureTask`实现了`RunnableFuture`接口,而`RunnableFuture`继承了`Runnable`和`Future`:

```javapublic class FutureTask\u003cV\u003e implements RunnableFuture\u003cV\u003e { // 构造器:接收Callable实例 public FutureTask(Callable\u003cV\u003e callable) { ... } // 实现Runnable的run()方法:内部会调用Callable的call(),并存储结果 @Override public void run() { ... }}

public interface RunnableFuture\u003cV\u003e extends Runnable, Future\u003cV\u003e { ... }```

因此,`FutureTask`的核心作用是:

+ 作为`Runnable`,可传入`Thread`启动线程;+ 作为`Future`,可通过`get()`方法获取`Callable`的返回值、判断任务是否完成、取消任务。

#### (3)核心关系流程图```mermaidgraph LR A[实现Callable接口\u003cbr\u003e重写call()(带返回值)] --\u003e|创建实例| B[Callable\u003cV\u003e 任务对象] B --\u003e|传入构造器| C[FutureTask\u003cV\u003e 实例\u003cbr\u003e(实现RunnableFuture)] C --\u003e|作为Runnable传入| D[Thread 线程对象] D --\u003e|调用start()| E[执行call()并存储结果] C --\u003e|调用get()| F[获取返回值/抛出异常] C --\u003e|调用isDone()| G[判断任务是否完成]```

### 2. 实现步骤1. 定义自定义类,实现`Callable\u003cV\u003e`接口(V为返回值类型);2. 重写`call()`方法,编写任务逻辑并返回结果(可抛异常);3. 创建`Callable\u003cV\u003e`实例(任务对象);4. 创建`FutureTask\u003cV\u003e`实例,将`Callable`实例传入;5. 创建`Thread`实例,将`FutureTask`实例传入;6. 调用`Thread`的`start()`方法启动线程;7. 调用`FutureTask`的`get()`方法获取`call()`的返回值(会阻塞当前线程,直到结果返回)。

### 3. 完整代码示例```javaimport java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;

/** * 方式3:实现Callable接口(带返回值)+ FutureTask */public class CallableImplDemo implements Callable\u003cInteger\u003e { private int start; private int end;

// 构造器:传入计算范围(示例:计算start到end的累加和) public CallableImplDemo(int start, int end) { this.start = start; this.end = end; }

// 1. 重写call()方法,带返回值、可抛异常 @Override public Integer call() throws Exception { int sum = 0; for (int i = start; i \u003c= end; i++) { sum += i; System.out.println(\"[\" + Thread.currentThread().getName() + \"] 正在计算:i=\" + i + \",当前和=\" + sum); Thread.sleep(50); // 模拟耗时 } return sum; // 返回计算结果 }

public static void main(String[] args) { // 2. 创建Callable任务对象(计算1-10的和) Callable\u003cInteger\u003e callableTask = new CallableImplDemo(1, 10); // 3. 创建FutureTask实例(桥梁:Callable → Runnable) FutureTask\u003cInteger\u003e futureTask = new FutureTask\u003c\u003e(callableTask); // 4. 创建Thread并传入FutureTask,启动线程 Thread thread = new Thread(futureTask, \"计算线程\"); thread.start(); // 5. 主线程逻辑:获取结果(会阻塞,直到子线程完成) try { // get():阻塞当前线程,直到call()执行完毕并返回结果 Integer result = futureTask.get(); System.out.println(\"\[\" + Thread.currentThread().getName() + \"] 子线程计算结果:1-10的和=\" + result); } catch (InterruptedException e) { // 线程被中断时抛出 e.printStackTrace(); } catch (ExecutionException e) { // call()方法抛出异常时,会被封装为ExecutionException抛出 System.out.println(\"子线程执行异常:\" + e.getCause().getMessage()); } }}```

**执行结果(部分)**:

```plain[计算线程] 正在计算:i=1,当前和=1[计算线程] 正在计算:i=2,当前和=3...[计算线程] 正在计算:i=10,当前和=55

[main] 子线程计算结果:1-10的和=55```

### 4. 关键注意点+ `get()`**方法的阻塞性**:`futureTask.get()`会阻塞调用线程(如主线程),直到子线程的`call()`方法执行完毕。若需避免阻塞,可先通过`futureTask.isDone()`判断任务是否完成,再决定是否调用`get()`。+ **异常处理**:`call()`方法抛出的异常会被封装为`ExecutionException`,需通过`e.getCause()`获取原始异常。+ **任务取消**:可通过`futureTask.cancel(boolean mayInterruptIfRunning)`取消任务: - `mayInterruptIfRunning=true`:若任务已在执行,会中断线程; - `mayInterruptIfRunning=false`:仅取消未开始的任务。

### 5. 优缺点分析| 优点 | 缺点 || --- | --- || 支持返回值:可获取线程执行的结果 | `get()`方法会阻塞:若子线程执行时间长,会阻塞调用线程 || 支持抛异常:可将任务中的异常抛出到调用线程处理 | 代码复杂度高:需额外创建FutureTask实例,步骤比Runnable多 || 可取消任务:通过`cancel()`方法终止未完成的任务 | 无法直接复用任务:若多个线程需执行同一任务,需创建多个Callable实例 |

## 四、使用线程池(高并发场景必备)无论是继承`Thread`、实现`Runnable`还是`Callable`,每次创建线程都会涉及“操作系统内核态与用户态的切换”,且线程执行完毕后会被销毁——频繁创建/销毁线程会带来巨大的性能开销。

为解决这个问题,Java提供了**线程池**(`ExecutorService`):线程池会预先创建一批线程,线程执行完任务后不会销毁,而是回到线程池等待下一个任务,从而实现线程的复用,降低性能开销。

### 1. 原理剖析#### (1)线程池的核心组件+ **线程池管理器(ExecutorService)**:负责线程池的创建、管理和销毁,提供提交任务的接口(如`submit()`、`execute()`)。+ **工作线程(Worker)**:线程池中预先创建的线程,负责执行任务,执行完后回到线程池等待新任务。+ **任务队列(BlockingQueue)**:当核心线程都在忙时,新提交的任务会被放入任务队列暂存。+ **拒绝策略(RejectedExecutionHandler)**:当任务队列满且线程池达到最大线程数时,对新任务的处理策略(如抛出异常、丢弃任务等)。

#### (2)线程池工作流程

☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

Java 并发编程】线程创建 6 种方式:Thread/Runnable/Callable 核心类解析+线程池使用说明
Java 并发编程】线程创建 6 种方式:Thread/Runnable/Callable 核心类解析+线程池使用说明

#### (3)核心参数解析创建线程池的核心类是`ThreadPoolExecutor`,其构造器包含7个核心参数,决定了线程池的行为:

| 参数名称 | 类型 | 作用 | 示例 || --- | --- | --- | --- || corePoolSize | int | 核心线程数:线程池长期维持的线程数量(即使空闲也不销毁) | 核心线程数=CPU核心数+1 || maximumPoolSize | int | 最大线程数:线程池允许创建的最大线程数(核心线程数+非核心线程数) | 最大线程数=CPU核心数*2 || keepAliveTime | long | 非核心线程空闲时间:超过此时间,非核心线程会被销毁 | 60L(单位:秒) || unit | TimeUnit | keepAliveTime的时间单位 | TimeUnit.SECONDS || workQueue | BlockingQueue | 任务队列:暂存待执行任务的队列 | LinkedBlockingQueue(无界队列)、ArrayBlockingQueue(有界队列) || threadFactory | ThreadFactory | 线程工厂:用于创建线程(可自定义线程名称、优先级等) | Executors.defaultThreadFactory() || handler | RejectedExecutionHandler | 拒绝策略:任务队列满且线程数达最大时的处理策略 | AbortPolicy(默认:抛出异常) |

### 2. 线程池的创建方式Java提供了两种创建线程池的方式:

1. **通过**`Executors`**工具类**:快速创建预设参数的线程池(适合简单场景,不推荐高并发场景);2. **直接创建**`ThreadPoolExecutor`**实例**:自定义核心参数(推荐,可避免`Executors`的潜在风险)。

#### (1)方式1:通过Executors工具类创建`Executors`提供了4种常用的线程池工厂方法:

| 线程池类型 | 工厂方法 | 核心参数特点 | 适用场景 || --- | --- | --- | --- || FixedThreadPool | `Executors.newFixedThreadPool(n)` | 核心线程数=最大线程数=n,队列无界 | 任务数量固定、需长期执行的场景(如服务端处理请求) || CachedThreadPool | `Executors.newCachedThreadPool()` | 核心线程数=0,最大线程数=Integer.MAX_VALUE,队列同步移交 | 任务数量多、执行时间短的场景(如临时任务处理) || SingleThreadExecutor | `Executors.newSingleThreadExecutor()` | 核心线程数=1,最大线程数=1,队列无界 | 需串行执行任务的场景(如日志写入、单线程处理请求) || ScheduledThreadPool | `Executors.newScheduledThreadPool(n)` | 核心线程数=n,最大线程数=Integer.MAX_VALUE | 定时/周期性执行任务的场景(如定时备份、心跳检测) |

**代码示例:FixedThreadPool**

```javaimport java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;

/** * 方式4:通过Executors创建FixedThreadPool */public class ExecutorsFixedDemo { public static void main(String[] args) { // 1. 创建FixedThreadPool(核心线程数=2,最大线程数=2) ExecutorService executorService = Executors.newFixedThreadPool(2); // 2. 提交3个任务(核心线程数=2,第3个任务会进入队列等待) for (int i = 1; i \u003c= 3; i++) { int taskId = i; // 匿名内部类引用外部变量需final或有效final // 提交Runnable任务(无返回值) executorService.execute(() -\u003e { System.out.println(\"[\" + Thread.currentThread().getName() + \"] 执行任务\" + taskId); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } }); } // 3. 提交Callable任务(有返回值,需用submit()方法) executorService.submit(() -\u003e { Thread.sleep(300); return \"Callable任务执行完毕\"; }).thenAccept(result -\u003e System.out.println(\"Callable任务结果:\" + result)); // Java 8+ CompletableFuture特性 // 4. 关闭线程池(重要:不关闭会导致JVM无法退出) // shutdown():等待所有已提交任务执行完毕后关闭线程池 executorService.shutdown(); // shutdownNow():立即关闭线程池,终止未执行的任务(慎用) // executorService.shutdownNow(); }}```

**执行结果(部分)**:

```plain[pool-1-thread-1] 执行任务1[pool-1-thread-2] 执行任务2[pool-1-thread-1] 执行任务3Callable任务结果:Callable任务执行完毕```

#### (2)方式2:直接创建ThreadPoolExecutor(推荐)`Executors`创建的线程池存在潜在风险(如`FixedThreadPool`的无界队列可能导致OOM),因此阿里巴巴《Java开发手册》推荐**直接使用**`ThreadPoolExecutor`**自定义线程池**,明确核心参数,避免资源耗尽。

**代码示例:自定义线程池**

```javaimport java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.ExecutorService;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;

/** * 方式4:直接创建ThreadPoolExecutor(推荐) */public class ThreadPoolExecutorDemo { public static void main(String[] args) { // 1. 定义核心参数 int corePoolSize = 2; // 核心线程数=2 int maximumPoolSize = 4; // 最大线程数=4 long keepAliveTime = 60L; // 非核心线程空闲60秒后销毁 TimeUnit unit = TimeUnit.SECONDS; // 时间单位:秒 // 任务队列:有界队列,容量=3(超过3个任务会创建非核心线程) ArrayBlockingQueue\u003cRunnable\u003e workQueue = new ArrayBlockingQueue\u003c\u003e(3); // 拒绝策略:当队列满且线程数达最大时,抛出异常(默认策略) ThreadPoolExecutor.AbortPolicy abortPolicy = new ThreadPoolExecutor.AbortPolicy(); // 2. 创建自定义线程池 ExecutorService customExecutor = new ThreadPoolExecutor( corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), // 默认线程工厂 abortPolicy ); // 3. 提交5个任务(核心2 + 队列3 = 5,无需创建非核心线程) for (int i = 1; i \u003c= 5; i++) { int taskId = i; customExecutor.execute(() -\u003e { System.out.println(\"[\" + Thread.currentThread().getName() + \"] 执行任务\" + taskId); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } }); } // 4. 提交第6个任务(队列满,创建第3个线程) customExecutor.execute(() -\u003e { System.out.println(\"[\" + Thread.currentThread().getName() + \"] 执行任务6(非核心线程)\"); }); // 5. 关闭线程池 customExecutor.shutdown(); }}```

### 3. 关键注意点+ **线程池的关闭**:必须调用`shutdown()`或`shutdownNow()`关闭线程池,否则线程池的核心线程会一直存活,导致JVM无法退出。 - `shutdown()`:温和关闭,等待所有已提交任务执行完毕后关闭; - `shutdownNow()`:强制关闭,立即中断所有正在执行的任务,并返回未执行的任务列表。+ **拒绝策略的选择**: - `AbortPolicy`(默认):抛出`RejectedExecutionException`,适合需要明确感知任务拒绝的场景; - `DiscardPolicy`:直接丢弃任务,不抛出异常,适合非核心任务; - `DiscardOldestPolicy`:丢弃队列中最旧的任务,然后提交新任务,适合任务有先后顺序的场景; - `CallerRunsPolicy`:由提交任务的线程(如主线程)自己执行任务,适合需要避免任务丢失的场景。+ **线程池参数的调优**:核心参数需根据业务场景调整,例如: - CPU密集型任务(如计算):核心线程数=CPU核心数+1(减少线程切换开销); - IO密集型任务(如网络请求、数据库操作):核心线程数=CPU核心数*2(利用IO等待时间复用线程)。

### 4. 优缺点分析| 优点 | 缺点 || --- | --- || 线程复用:避免频繁创建/销毁线程,降低性能开销 | 配置复杂:需根据业务场景合理设置核心参数(如队列大小、最大线程数) || 控制并发:通过核心参数限制最大并发数,避免系统资源耗尽 | 任务堆积风险:若任务执行速度慢,队列可能堆积导致OOM(需用有界队列) || 管理便捷:提供统一的任务提交和线程管理接口 | 线程泄漏风险:若忘记关闭线程池,核心线程会一直存活,浪费资源 || 支持异步:可结合`Callable`获取任务结果,支持定时任务 | 调试难度高:多线程并发问题(如死锁、线程安全)排查复杂 |

## 五、CompletableFuture(Java 8+异步神器)`Callable+FutureTask`虽然支持返回值,但存在一个痛点:**获取结果时需要主动调用**`get()`**方法,会阻塞线程**。为解决这个问题,Java 8引入了`CompletableFuture`——它基于`FutureTask`扩展,支持**异步回调**,无需阻塞即可处理线程执行结果,极大简化了异步编程。

### 1. 原理剖析`CompletableFuture`实现了`CompletionStage`和`Future`接口:

+ `CompletionStage`:定义了异步任务的“阶段”,支持链式调用(如`thenAccept()`、`thenApply()`),一个阶段完成后自动触发下一个阶段;+ `Future`:继承了`Future`的核心能力(如`get()`、`cancel()`)。

`CompletableFuture`默认使用`ForkJoinPool.commonPool()`(一个共享的线程池)执行任务,也可自定义线程池。

### 2. 核心方法分类`CompletableFuture`的方法众多,按功能可分为“创建异步任务”和“处理任务结果”两类:

| 方法类型 | 核心方法 | 作用 || --- | --- | --- || 创建异步任务(无返回值) | `runAsync(Runnable runnable)` | 使用默认线程池执行任务 || | `runAsync(Runnable runnable, Executor executor)` | 使用自定义线程池执行任务 || 创建异步任务(有返回值) | `supplyAsync(Supplier\u003cU\u003e supplier)` | 使用默认线程池执行任务,返回结果 || | `supplyAsync(Supplier\u003cU\u003e supplier, Executor executor)` | 使用自定义线程池执行任务,返回结果 || 处理任务结果(回调) | `thenAccept(Consumer\u003c? super U\u003e action)` | 任务完成后,消费结果(无返回值) || | `thenApply(Function\u003c? super U, ? extends V\u003e fn)` | 任务完成后,转换结果(有返回值,可链式调用) || | `exceptionally(Function\u003cThrowable, ? extends U\u003e fn)` | 任务异常时,处理异常并返回默认值 |

### 3. 完整代码示例#### (1)示例1:无返回值的异步任务```javaimport java.util.concurrent.CompletableFuture;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;

/** * 方式5:CompletableFuture(无返回值) */public class CompletableFutureRunAsyncDemo { public static void main(String[] args) { // 1. 创建自定义线程池(推荐,避免使用默认的ForkJoinPool) ExecutorService executor = Executors.newFixedThreadPool(2); // 2. 使用runAsync创建无返回值的异步任务 CompletableFuture\u003cVoid\u003e future = CompletableFuture.runAsync(() -\u003e { System.out.println(\"[\" + Thread.currentThread().getName() + \"] 执行异步任务(无返回值)\"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }, executor); // 传入自定义线程池 // 3. 任务完成后的回调(无需阻塞,自动执行) future.thenRun(() -\u003e { System.out.println(\"[\" + Thread.currentThread().getName() + \"] 异步任务执行完毕(回调)\"); }); // 4. 主线程继续执行其他逻辑(无阻塞) System.out.println(\"[\" + Thread.currentThread().getName() + \"] 主线程执行其他任务\"); // 5. 关闭线程池(注意:需等待异步任务完成,否则可能中断任务) executor.shutdown(); }}```

**执行结果**:

```plain[main] 主线程执行其他任务[pool-1-thread-1] 执行异步任务(无返回值)[pool-1-thread-1] 异步任务执行完毕(回调)```

#### (2)示例2:有返回值的异步任务+链式回调```javaimport java.util.concurrent.CompletableFuture;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;

/** * 方式5:CompletableFuture(有返回值+链式回调) */public class CompletableFutureSupplyAsyncDemo { public static void main(String[] args) { ExecutorService executor = Executors.newSingleThreadExecutor(); // 1. supplyAsync:创建有返回值的异步任务(返回String) CompletableFuture\u003cString\u003e future = CompletableFuture.supplyAsync(() -\u003e { System.out.println(\"[\" + Thread.currentThread().getName() + \"] 执行异步任务(有返回值)\"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(\"任务被中断\", e); } return \"Hello, CompletableFuture!\"; // 返回结果 }, executor); // 2. thenApply:转换结果(String → Integer,返回新的CompletableFuture) CompletableFuture\u003cInteger\u003e lengthFuture = future.thenApply(result -\u003e { System.out.println(\"[\" + Thread.currentThread().getName() + \"] 转换结果:原结果=\" + result); return result.length(); // 将字符串转换为长度(Integer) }); // 3. thenAccept:消费转换后的结果(无返回值) lengthFuture.thenAccept(length -\u003e { System.out.println(\"[\" + Thread.currentThread().getName() + \"] 最终结果:字符串长度=\" + length); }); // 4. exceptionally:处理异常(若任务抛出异常,返回默认值) future.exceptionally(ex -\u003e { System.out.println(\"任务执行异常:\" + ex.getMessage()); return \"默认值\"; // 异常时的返回值 }); executor.shutdown(); }}```

**执行结果**:

```plain[pool-1-thread-1] 执行异步任务(有返回值)[pool-1-thread-1] 转换结果:原结果=Hello, CompletableFuture![pool-1-thread-1] 最终结果:字符串长度=23```

### 4. 核心优势:非阻塞与链式编程与传统的`FutureTask`相比,`CompletableFuture`的核心优势在于:

1. **非阻塞回调**:无需调用`get()`阻塞线程,任务完成后自动触发回调方法;2. **链式调用**:支持多个回调阶段的链式组合(如`supplyAsync() → thenApply() → thenAccept()`),代码更简洁;3. **多任务组合**:支持多个异步任务的协同(如`allOf()`:等待所有任务完成;`anyOf()`:等待任意一个任务完成);4. **自定义线程池**:可避免默认`ForkJoinPool`的资源竞争问题。

### 5. 优缺点分析| 优点 | 缺点 || --- | --- || 非阻塞回调:无需阻塞线程,提高程序吞吐量 | 学习成本高:方法众多,需理解`CompletionStage`的阶段模型 || 链式编程:简化多步骤异步任务的代码逻辑 | 调试难度高:链式回调的异常堆可能不清晰 || 支持多任务组合:轻松实现复杂的异步协同逻辑 | 默认线程池风险:若使用默认`ForkJoinPool`,高并发下可能导致资源竞争 || 支持异常处理:通过`exceptionally()`统一处理异常 | 内存泄漏风险:若忘记关闭自定义线程池,会导致资源浪费 |

## 六、总结:如何选择合适的线程创建方式?Java提供的6种线程创建方式,各有适用场景,选择的核心依据是**业务需求(是否需要返回值、并发量、是否异步)** 和**性能要求**。以下是各方式的适用场景汇总:

| 线程创建方式 | 核心特点 | 适用场景 || --- | --- | --- || 继承Thread类 | 实现简单,单继承限制 | 简单的并发场景,无其他继承需求 || 实现Runnable接口 | 无继承限制,任务解耦 | 普通并发场景,需复用任务或多实现接口 || 实现Callable+FutureTask | 支持返回值和异常,需阻塞获取结果 | 需获取线程执行结果的场景(如计算任务) || 线程池(ExecutorService) | 线程复用,控制并发,高性能 | 高并发场景(如服务端处理请求、批量任务) || CompletableFuture | 非阻塞回调,链式编程,异步协同 | Java 8+的异步场景(如微服务调用、IO密集型任务) |

### 关键建议1. **避免频繁创建独立线程**:除非是简单的一次性任务,否则优先使用线程池或`CompletableFuture`,避免线程创建/销毁的性能开销;2. **高并发场景首选线程池**:通过自定义`ThreadPoolExecutor`明确核心参数,避免`Executors`的潜在风险;3. **异步回调用CompletableFuture**:Java 8及以上版本,若需异步处理结果,优先使用`CompletableFuture`,简化代码并提高吞吐量;4. **线程安全是前提**:无论选择哪种方式,都需注意线程安全(如使用`synchronized`、`ConcurrentHashMap`等),避免数据竞争问题。

通过本文的详细拆解,相信你已掌握Java线程创建的所有方式。在实际开发中,需结合业务场景灵活选择,才能写出高效、稳定的并发代码。

以上就是Java 并发编程】线程创建 6 种方式:Thread/Runnable/Callable 核心类解析+线程池使用说明的详细内容,更多请关注php中文网其它相关文章!

编程速学教程(入门课程)
编程速学教程(入门课程)

编程怎么学习?编程怎么入门?编程在哪学?编程怎么学才快?不用担心,这里为大家提供了编程速学教程(入门课程),有需要的小伙伴保存下载就能学习啦!

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号