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

## 引言:线程与%ignore_a_1%并发的核心在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() + “] 主线程执行完毕”); }}“`

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

美间AI 美间AI

美间AI:让设计更简单

美间AI 261 查看详情 美间AI

“`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 Callableu003cVu003e { V call() throws Exception; // 有返回值、可抛checked异常}“`

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

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

public interface RunnableFutureu003cVu003e extends Runnable, Futureu003cVu003e { … }“`

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

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

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

### 2. 实现步骤1. 定义自定义类,实现`Callableu003cVu003e`接口(V为返回值类型);2. 重写`call()`方法,编写任务逻辑并返回结果(可抛异常);3. 创建`Callableu003cVu003e`实例(任务对象);4. 创建`FutureTasku003cVu003e`实例,将`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 Callableu003cIntegeru003e { 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的和) Callableu003cIntegeru003e callableTask = new CallableImplDemo(1, 10); // 3. 创建FutureTask实例(桥梁:Callable → Runnable) FutureTasku003cIntegeru003e futureTask = new FutureTasku003cu003e(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个任务会创建非核心线程) ArrayBlockingQueueu003cRunnableu003e workQueue = new ArrayBlockingQueueu003cu003e(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(Supplieru003cUu003e supplier)` | 使用默认线程池执行任务,返回结果 || | `supplyAsync(Supplieru003cUu003e supplier, Executor executor)` | 使用自定义线程池执行任务,返回结果 || 处理任务结果(回调) | `thenAccept(Consumeru003c? super Uu003e action)` | 任务完成后,消费结果(无返回值) || | `thenApply(Functionu003c? super U, ? extends Vu003e fn)` | 任务完成后,转换结果(有返回值,可链式调用) || | `exceptionally(Functionu003cThrowable, ? extends Uu003e 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创建无返回值的异步任务 CompletableFutureu003cVoidu003e 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) CompletableFutureu003cStringu003e 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) CompletableFutureu003cIntegeru003e 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 核心类解析+线程池使用说明的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/881474.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
UC浏览器清除历史记录无效怎么办 UC浏览器隐私数据清理方法
上一篇 2025年11月28日 10:11:03
网易邮箱163com登录_网易163邮箱官方登录通道
下一篇 2025年11月28日 10:11:05

相关推荐

  • iPhone16如何设置返回导航键

    一、打开设置界面 在手机主屏上找到标有“设置”的应用图标,轻触进入。这里是管理iPhone各项功能的核心区域。 二、进入系统管理菜单 在设置列表中向下滑动,定位到“系统”选项并点击进入。该菜单集中了与设备基础操作相关的配置功能。 三、查找导航功能设置 在“系统”页面中,寻找名为“导航”的功能入口。该…

    2026年8月26日
    000
  • 80 PLUS 认证等级背后的真相:转换效率与纹波测试

    80 PLUS认证衡量电源转换效率,等级越高效率越高,从白牌到钛金牌及新增红宝石标准,要求逐步提升,其中红宝石在50%负载下效率需达96.5%以上;高效率降低能耗与发热,延长硬件寿命。同时,纹波测试评估输出稳定性,+12V纹波应低于120mV,优质电源纹波更低,确保系统稳定运行。高效与低纹波结合,提…

    2026年8月26日
    000
  • 如何举报抖音用户成功率高?抖音怎样举报才能100%成功

    在如今这个信息爆炸的时代,抖音已经成为许多人生活中不可或缺的一部分。随着用户数量的激增,一些不良信息、违规内容也混杂其中。为了维护一个健康、积极的网络环境,了解如何举报抖音用户变得尤为重要。本文将为你详细解析如何提高举报成功率,让你轻松应对各类违规行为。 一、了解举报机制 在开始举报之前,我们需要先…

    2026年8月26日
    000
  • 华硕主机散热系统设计及机箱风道优化技巧

    华硕主机散热系统设计及机箱风道优化技巧华硕主机散热系统设计及机箱风道优化技巧华硕主机散热系统设计及机箱风道优化技巧华硕主机散热系统设计及机箱风道优化技巧

    合理布局风扇和优化风道可提升华硕主机散热效率,具体建议如下:1. 华硕主机预装风扇配置通常为前置进风+后置出风,部分机型加装顶部或底部风扇,中高端平台需视温度增加风扇;2. 风道布局推荐均压或正压,兼顾散热与防尘;3. 根据功耗配置风扇数量和位置,低、中、高功耗平台分别配置1个、3个及以上风扇;4.…

    2026年8月26日 用户投稿
    000
  • Java中堆内存和栈内存的区别及内存管理机制

    Java中堆内存和栈内存的区别及内存管理机制Java中堆内存和栈内存的区别及内存管理机制Java中堆内存和栈内存的区别及内存管理机制Java中堆内存和栈内存的区别及内存管理机制

    堆内存用于存储对象实例,栈内存用于方法调用和局部变量。1. 堆内存由垃圾回收器管理,线程共享,生命周期长,适合存储动态分配的对象;2. 栈内存自动管理,线程私有,生命周期短,适合存储局部变量和方法调用帧;3. 区分两者是为了优化内存管理和性能;4. 堆溢出可通过分析内存泄漏、优化代码、增加堆内存等解…

    2026年8月26日 用户投稿
    100
  • 电脑主机启动不起来怎么回事 五种解决方法

    电脑主机启动不起来怎么回事 五种解决方法电脑主机启动不起来怎么回事 五种解决方法电脑主机启动不起来怎么回事 五种解决方法电脑主机启动不起来怎么回事 五种解决方法

    电脑按下电源键后毫无反应或无法正常启动,确实让人感到困扰。那么,电脑主机无法启动的原因究竟是什么?本文将为你梳理常见的故障表现,深入分析可能原因,并提供五种实用的解决方法,助你快速排查问题,恢复电脑正常使用。 一、电脑主机无法启动的常见现象 “无法启动”这一问题在实际中可能表现为多种情况,主要包括:…

    2026年8月26日 用户投稿
    200
  • 多服务器环境下Session共享方案

    多服务器环境下需要session共享以确保用户体验的连贯性和数据的一致性。实现方案包括:1) 使用redis或memcached进行集中式session管理,优点是高效处理大规模数据,但增加了系统复杂性和单点故障风险;2) 使用session复制,通过服务器间同步session数据,优点是无需额外存…

    2026年8月26日
    000
  • 私信怎样做自动回复?私信怎样做自动回复内容

    在当今快节奏的生活环境中,时间显得尤为珍贵。对于运营社交媒体账号、电商平台店铺或个人公众号的用户而言,如何高效应对海量私信已成为一个不可忽视的挑战。接下来,本文将为你全面解析私信自动回复的设置方法,助你提升效率,轻松应对日常沟通。 一、私信自动回复的优势 节约时间成本:通过预设常见问题的答复,系统可…

    2026年8月26日
    400
  • Java中反射机制的优缺点及适用场景探讨

    Java中反射机制的优缺点及适用场景探讨Java中反射机制的优缺点及适用场景探讨Java中反射机制的优缺点及适用场景探讨Java中反射机制的优缺点及适用场景探讨

    反射是一种让程序在运行时动态获取类信息并操作类或对象的能力,它使程序能够检查、修改类的结构并调用其方法和属性。优势包括:1. 提供动态性与灵活性;2. 支持框架设计如spring的依赖注入;3. 实现插件系统的动态加载;4. 构建动态代理以执行额外操作;5. 开发通用工具处理各种类型对象。劣势有:1…

    2026年8月26日 用户投稿
    000
  • PCIe Riser 延长线对显卡性能的损耗实测

    使用合格的PCIe Riser延长线对显卡性能影响极小,实测显示性能损耗在1%-2%之间,帧数波动不超过1-2帧,基本处于误差范围内,实际体验无感知;即便是高端显卡如RTX 4090在PCIe 5.0平台搭配4.0延长线,数据传输速率和极限负载表现也无明显差异;选购时需注意版本匹配、供电连接可靠,并…

    2026年8月26日
    100
  • 系统文件损坏怎么修复 4步搞定

    系统文件损坏怎么修复 4步搞定系统文件损坏怎么修复 4步搞定系统文件损坏怎么修复 4步搞定系统文件损坏怎么修复 4步搞定

    在使用电脑时,系统文件损坏是不少用户经常遇到的困扰。这类问题可能引发程序异常关闭、系统蓝屏,甚至导致系统无法启动。别担心,下面为大家整理了几种实用的修复方式,一起来了解一下吧~ 一、使用系统自带的SFC(系统文件检查器)进行修复 SFC是Windows系统内置的诊断工具,能够自动检测并修复受损的系统…

    2026年8月26日 用户投稿
    000
  • 搜狗输入法怎么换皮肤_搜狗输入法皮肤下载与更换

    换搜狗输入法皮肤需先找到入口,电脑端点击状态栏“衣服”或“S”图标进入皮肤盒子,预览后一键启用;2. 手机端在输入框调出键盘,点击“S”标志进入“皮肤”选项,下载即可自动应用;3. 支持手动安装.skin文件,可通过官网下载或本地导入,部分皮肤可自动更新样式。 想给搜狗输入法换个新皮肤,操作很简单,…

    2026年8月26日
    000
  • 发私信不能自动回复?私信设置自动回复

    在信息高速流通的今天,我们每天都会收到大量私信。为了提升沟通效率,不少个人用户和商家都启用了自动回复功能。然而,有时却发现发送私信后并未触发自动回复,令人困惑。究竟是什么原因导致这一现象?本文将深入剖析自动回复失效的四大核心原因。 一、网络连接异常 首要考虑的因素便是网络状况。若网络信号弱或连接不稳…

    2026年8月26日
    000
  • Swoft框架的依赖注入与AOP

    在swoft框架中,依赖注入和aop通过注解协同工作,提升代码的可维护性和可扩展性。1)依赖注入通过@inject注解实现组件解耦,提高代码的可测试性和灵活性。2)aop通过@aspect和@around注解实现横切关注点的分离,如日志记录,增强代码的模块化和可重用性。 在Swoft框架中,依赖注入…

    2026年8月26日
    600
  • 告别PHP命令行参数混乱:nategood/commando助你打造优雅CLI工具!

    可以通过一下地址学习composer:学习地址 命令行工具开发的“痛点” 作为php开发者,我们经常需要编写一些命令行脚本来执行自动化任务、数据处理或系统维护。一开始,我们可能习惯于直接使用php内置的 $argv 超全局变量来获取命令行参数,或者尝试使用 getopt() 函数进行稍微结构化的解析…

    用户投稿 2026年8月26日
    000
  • 摄像机怎么恢复出厂设置_摄像机恢复出厂设置的操作步骤与注意事项

    恢复出厂设置可解决登录问题或清除配置,适用于转售设备。首先通过物理按键复位,适用于带Reset孔的摄像机,通电状态下长按5-10秒至指示灯红蓝闪烁即可;其次可通过Web界面操作,需登录同网段摄像机IP,在系统维护中点击恢复出厂设置;第三种方式为手机APP远程重置,解绑设备后重新添加并勾选强制恢复选项…

    2026年8月26日
    200
  • 抖音平台优惠券会给卖家补贴吗?抖音平台优惠券会给卖家补贴吗是真的吗

    随着移动互联网的迅猛发展,抖音已成长为国内最具影响力的短视频社交平台之一。目前,抖音的日活跃用户已突破5亿大关,海量用户每天在平台上浏览、分享和创作短视频内容。与此同时,平台的营销手段也日益丰富,其中优惠券活动成为商家常用的推广方式之一。那么,抖音平台推出的优惠券活动是否真的会为卖家提供补贴?本文将…

    2026年8月26日
    000
  • 如何解决HEIC/AVIF图片转换难题?使用Composer和heif-converter轻松搞定!

    可以通过一下地址学习composer:学习地址 告别 HEIC/AVIF 图片兼容性烦恼:用 Composer 玩转 heif-converter 相信很多朋友都有过这样的经历:朋友用 iphone 拍了张照片发给你,结果你发现它是个 .heic 文件。或者,你在网上下载了一些高质量的图片,发现它们…

    用户投稿 2026年8月26日
    000
  • 电脑找不到vcruntime140_1.dll无法继续执行代码的解决方法

    电脑找不到vcruntime140_1.dll无法继续执行代码的解决方法电脑找不到vcruntime140_1.dll无法继续执行代码的解决方法电脑找不到vcruntime140_1.dll无法继续执行代码的解决方法电脑找不到vcruntime140_1.dll无法继续执行代码的解决方法

    打开某个软件或游戏时,突然提示“找不到vcruntime140_1.dll,无法继续执行代码”?这类错误通常源于系统缺少必要的运行库支持。接下来,我们一起来看看几种实用的解决方案。 一、为什么会提示“vcruntime140_1.dll缺失”? vcruntime140_1.dll 是微软 Visu…

    2026年8月26日 用户投稿
    000
  • iOS/iPadOS 17 Beta 7更新有哪些升级建议

    不建议在主力设备上安装iOS/iPadOS 17 Beta 7。该测试版虽较早期版本稳定,但仍可能存在影响通话、信息、应用使用的bug,常见问题包括续航下降、发热、Wi-Fi异常及App闪退,尤其银行类应用或受限。升级前必须通过iCloud或电脑完成完整数据备份,并记录已安装应用以便恢复。可尝鲜待机…

    2026年8月26日
    000

发表回复

登录后才能评论
关注微信