Java多线程入门指南

一、 Thread 和 ThreadLocal 区别

ThreadLocalThread 是 Java 中两个与线程相关的概念,它们在多线程编程中有不同的作用和使用场景。

1. Thread

Thread 是 Java 中表示一个线程的类。每个 Thread 对象表示一个单独的执行路径,即程序中的一个执行流。Java 中的多线程是通过创建 Thread 对象来实现的。每个 Thread 可以拥有自己的执行逻辑,通常是通过继承 Thread 类或实现 Runnable 接口来定义线程执行的任务。

特点:

  • 每个 Thread 对象代表一个独立的执行线程。
  • 线程是操作系统调度的基本单元,操作系统会管理线程的生命周期(如调度、切换、终止等)。
  • 每个线程有自己的堆栈和局部变量,但它们共享 JVM 的堆内存和其他全局资源。

示例:

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

public class ThreadExample {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();  // 启动线程
    }
}

2. ThreadLocal

ThreadLocal 是 Java 提供的一种用于实现线程局部存储(Thread-Local Storage, TLS)的类。每个 ThreadLocal 变量在每个线程中都有一个独立的副本,每个线程访问自己副本的数据,不会与其他线程发生冲突。

特点:

  • ThreadLocal 允许每个线程都拥有一个独立的变量副本,因此每个线程都可以独立地存储和读取该变量的值。
  • 它通常用于避免线程间的竞争条件,因为每个线程访问的都是自己专有的副本。
  • ThreadLocal 变量的生命周期由线程的生命周期决定,在线程结束时,这些局部变量会自动回收。

使用场景:

  • 常用于保存与当前线程相关的数据(例如,数据库连接、用户会话等),避免多个线程共享同一个对象导致的线程安全问题。
  • 它能够避免在多线程环境中使用 synchronized 来同步数据,减少了资源争用,提高了性能。

示例:

public class ThreadLocalExample {
    // 创建一个 ThreadLocal 变量
    private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 1);

    public static void main(String[] args) {
        Runnable task = () -> {
            // 每个线程都有自己独立的副本
            System.out.println(Thread.currentThread().getName() + " ThreadLocal value: " + threadLocal.get());
            threadLocal.set(threadLocal.get() + 1);  // 修改当前线程的值
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);

        t1.start();
        t2.start();
    }
}

在这个例子中,每个线程都会有自己的 threadLocal 变量副本,修改一个线程的值不会影响另一个线程。

总结区别:

特性 Thread ThreadLocal
用途 用于创建和管理独立的线程 用于为每个线程提供独立的变量副本
作用范围 代表一个独立的线程,用于执行任务 允许每个线程持有自己独立的变量副本
线程间共享数据 各线程之间共享数据 各线程的数据是隔离的,不会相互影响
存储位置 存储在线程的堆栈中,线程之间共享全局资源 存储在线程局部的内存空间,每个线程有自己的副本
使用场景 需要并发执行的任务 需要为每个线程提供独立数据的场景

何时使用:

  • 使用 Thread:当需要并行处理多个任务时,例如启动多个线程来处理不同的工作负载。
  • 使用 ThreadLocal:当每个线程都需要保持一些独立的数据(如数据库连接、用户会话等),不希望线程之间相互干扰时,可以使用 ThreadLocal

二、Thread 的 start() 和 run() 区别

看看以下这段关键代码:

Thread thread = new Thread();
thread.start(); // 启动线程,会在新的线程中调用 run 方法,实现异步执行
thread.run();   // 当前线程直接执行 run 方法,不会启动新线程

1. thread.start();

  • 启动一个新线程。
  • 会调用线程的 run() 方法,但该调用是在新的线程中执行的,实现实际异步执行。
  • 也就是说,start() 是实现 异步/多线程 的入口。

2. thread.run();

  • 只是一个普通的方法调用,不会创建新线程。
  • run() 方法会在 当前线程(通常是主线程) 中执行,不具备并发性。

三、示例说明

public class MyThread extends Thread {
    public void run() {
        System.out.println("Thread name: " + Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread();

        thread.run();   // 当前线程执行 run 方法,输出线程名为 main
        thread.start(); // 启动新线程,输出线程名为 Thread-0(或其他)
    }
}

可能的输出:

Thread name: main
Thread name: Thread-0

解释:

  • 第一次调用 run() 是在主线程中,所以输出是 “main”。
  • 第二次调用 start() 创建了新线程,输出为 JVM 分配的线程名,如 “Thread-0”。

四、总结区别

方法 是否新线程 是否并发执行
start() ☑ 是 ☑ 是
run() ❌ 否 ❌ 否

五、提示

  • 在线程实现中,始终该通过 start() 来启动线程,不要直接调用 run()
  • 直接调用 run() 只是在当前线程中执行一段方法,绝非实际的多线程。

六、扩展及使用场景

1. 继承 Thread 类

适用于需要自定义线程类,且线程行为较简单的情况。

class MyThread extends Thread {
    public void run() {
        // 线程要执行的任务
        System.out.println("线程执行: " + Thread.currentThread().getName());
    }
}

public class Demo {
    public static void main(String[] args) {
        new MyThread().start(); // 启动线程
    }
}

2. 实现 Runnable 接口

推荐用于资源共享(多个线程访问同一实例),避免多继承问题。

class MyRunnable implements Runnable {
    public void run() {
        // 线程任务
        System.out.println("线程执行: " + Thread.currentThread().getName());
    }
}

public class Demo {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunnable());
        thread.start(); // 启动线程
    }
}

3. 实现 Callable 配合 Future

适用于需要线程返回结果、抛出异常的场景。

import java.util.concurrent.*;

class MyCallable implements Callable<String> {
    public String call() {
        // 返回值任务
        return "线程返回值: " + Thread.currentThread().getName();
    }
}

public class Demo {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(new MyCallable()); // 提交任务
        System.out.println(future.get()); // 获取返回结果
        executor.shutdown(); // 关闭线程池
    }
}

4. 使用线程池 ExecutorService

适用于高频率创建线程的场景,避免频繁创建/销毁线程带来的开销。

ExecutorService executor = Executors.newFixedThreadPool(2); // 创建固定线程池
executor.execute(() -> System.out.println("线程池执行: " + Thread.currentThread().getName()));
executor.shutdown(); // 关闭线程池

七、线程锁的几种方式

1. synchronized 关键字(隐式锁)

适用于简单的同步需求。

public synchronized void safeMethod() {
    // 加锁区域,保证线程安全
    System.out.println("线程安全: " + Thread.currentThread().getName());
}

2. ReentrantLock(显式锁)

适用于需要尝试加锁、定时锁、可中断锁等高级场景。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockExample {
    private final Lock lock = new ReentrantLock();

    public void safeMethod() {
        lock.lock(); // 显式加锁
        try {
            System.out.println("线程安全执行: " + Thread.currentThread().getName());
        } finally {
            lock.unlock(); // 释放锁
        }
    }
}

3. 双向锁机制(互斥同步)

用于描述两个线程互相等待对方释放资源,容易导致死锁。

public class DeadlockDemo {
    private static final Object resourceA = new Object();
    private static final Object resourceB = new Object();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (resourceA) {
                System.out.println("线程1获取了资源A");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                synchronized (resourceB) {
                    System.out.println("线程1获取了资源B");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (resourceB) {
                System.out.println("线程2获取了资源B");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                synchronized (resourceA) {
                    System.out.println("线程2获取了资源A");
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}

此例演示了一个双向锁死锁问题

  • 线程1 持有 A 等待 B,线程2 持有 B 等待 A。
  • 避免死锁的方式包括资源排序、超时控制、尝试锁等策略。

4. ReadWriteLock(读写锁)

适用于读多写少的场景,提高并发性能。

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteExample {
    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void read() {
        lock.readLock().lock(); // 获取读锁
        try {
            System.out.println("读取操作: " + Thread.currentThread().getName());
        } finally {
            lock.readLock().unlock(); // 释放读锁
        }
    }

    public void write() {
        lock.writeLock().lock(); // 获取写锁
        try {
            System.out.println("写入操作: " + Thread.currentThread().getName());
        } finally {
            lock.writeLock().unlock(); // 释放写锁
        }
    }
}

5. 双重检查锁定(DCL)

适用于单例模式等延迟初始化场景,保证线程安全的同时减少同步开销。

public class DoubleCheckedLockingSingleton {
    // 使用 volatile 关键字确保可见性和禁止指令重排
    private static volatile DoubleCheckedLockingSingleton instance;

    // 私有构造函数,防止外部实例化
    private DoubleCheckedLockingSingleton() {}

    // 双重检查锁定获取单例实例的方法
    public static DoubleCheckedLockingSingleton getInstance() {
        // 第一次检查,未加锁
        if (instance == null) {
            // 同步块,加锁
            synchronized (DoubleCheckedLockingSingleton.class) {
                // 第二次检查,加锁后
                if (instance == null) {
                    instance = new DoubleCheckedLockingSingleton();
                }
            }
        }
        return instance;
    }

    public static void main(String[] args) {
        // 测试获取单例实例
        DoubleCheckedLockingSingleton singleton1 = DoubleCheckedLockingSingleton.getInstance();
        DoubleCheckedLockingSingleton singleton2 = DoubleCheckedLockingSingleton.getInstance();
        System.out.println(singleton1 == singleton2); 
    }
}    

双重检查锁定使用 volatile 确保指令不会被重排序,synchronized 保证线程安全。


八、线程中断机制(Interrupt)

Java 中线程的中断是一种协作机制,主要用于通知线程“你应该停止了”,而非强制终止线程。

1. 中断线程的基本方法:

Thread thread = new Thread(() -> {
    while (!Thread.currentThread().isInterrupted()) {
        // 执行任务
    }
    System.out.println("线程被中断,退出循环");
});
thread.start();

// 中断线程
thread.interrupt();

2. 判断线程是否中断

  • isInterrupted():判断线程是否被中断,不清除中断标志。
  • Thread.interrupted():判断并清除当前线程的中断状态。
Thread thread = new Thread(() -> {
    while (true) {
        if (Thread.interrupted()) {
            System.out.println("检测到中断状态,退出线程");
            break;
        }
    }
});
thread.start();
thread.interrupt();

3. 中断阻塞中的线程(如 sleep/wait/join)

Thread thread = new Thread(() -> {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        System.out.println("线程在 sleep 时被中断");
    }
});
thread.start();
thread.interrupt();

4. 使用中断的最佳实践

  • 不推荐使用 stop() 等方法强制终止线程。
  • 建议线程定期检查 isInterrupted()Thread.interrupted(),优雅退出。
  • 阻塞方法如 sleep()wait() 等被中断会抛出 InterruptedException,可用 try-catch 处理。

九、线程池与 Spring 定时任务机制

1. 线程池基础概念与核心参数

线程池用于重用线程资源,避免频繁创建和销毁线程造成资源浪费。 Java 提供的核心线程池类是 java.util.concurrent.ThreadPoolExecutor

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    2,      // corePoolSize 核心线程数
    4,      // maximumPoolSize 最大线程数
    60,     // keepAliveTime 非核心线程的空闲存活时间
    TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(100), // 工作队列
    new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);

线程池构造函数的七大参数如下:

  1. corePoolSize:核心线程数,始终存活,即使处于空闲状态。
  2. maximumPoolSize:线程池所能容纳的最大线程数,包含核心线程数。
  3. keepAliveTime:线程最大空闲时间,超过该时间的非核心线程将被回收。
  4. unit:keepAliveTime 的时间单位(如秒、毫秒等)。
  5. workQueue:任务队列,用于缓存提交的任务。
  6. threadFactory:线程工厂,用户可以自定义线程的创建方式,如设置名称、优先级等。
  7. handler(RejectedExecutionHandler) :拒绝策略,当线程池和队列都满了时处理新任务的方式。

常用拒绝策略有:

  • AbortPolicy:默认策略,抛出异常。
  • CallerRunsPolicy:调用者线程执行任务。
  • DiscardOldestPolicy:丢弃队列中最老的任务,尝试执行新任务。
  • DiscardPolicy:直接丢弃任务,不抛异常。

2. 线程池使用示例

ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(() -> {
    // 执行任务逻辑
    System.out.println("线程池任务: " + Thread.currentThread().getName());
});
executor.shutdown(); // 关闭线程池,等待任务执行完毕

3. Spring 定时器线程池原理

Spring 中使用 @Scheduled 注解可轻松实现定时任务,底层依赖线程池调度。

@Component
public class MyScheduler {
    @Scheduled(fixedRate = 5000) // 每隔 5 秒执行一次
    public void task() {
        System.out.println("定时任务执行: " + Thread.currentThread().getName());
    }
}

SpringBoot 自动配置类 TaskSchedulingAutoConfiguration 会创建一个 ThreadPoolTaskScheduler,其本质是基于 ScheduledThreadPoolExecutor 实现定时调度的线程池。

可以通过配置修改线程池参数:

spring:
  task:
    scheduling:
      pool:
        size: 10

Spring 定时线程池执行逻辑简述:

  1. 项目启动时读取 @Scheduled 注解方法。
  2. 将任务封装为 ScheduledMethodRunnable
  3. 利用 ScheduledThreadPoolExecutor 按固定频率执行。
  4. 线程执行期间支持异常处理、线程重用和日志打印。

4. 常见线程池类型及使用场景

Java 提供了几种常用线程池工厂方法,适用于不同的场景:

线程池类型 创建方式 使用场景 优点 缺点
FixedThreadPool Executors.newFixedThreadPool(n) 固定数量线程,适用于任务量稳定的场景 限制线程数量,节省资源 无法动态扩容,任务过多可能阻塞
CachedThreadPool Executors.newCachedThreadPool() 高频短任务,适合处理大量瞬时请求 按需扩展线程数,响应快 线程数不受控,可能耗尽资源
SingleThreadExecutor Executors.newSingleThreadExecutor() 单线程串行执行任务,适用于日志记录等 顺序执行,线程安全 性能低,单点故障风险高
ScheduledThreadPool Executors.newScheduledThreadPool(n) 定时任务、周期任务调度 支持延迟和周期性执行 线程数固定,调度复杂
WorkStealingPool Executors.newWorkStealingPool() 多核 CPU 环境下高吞吐计算密集型任务 自动负载均衡,效率高 JDK 8+ 支持,控制较少

5. 线程池性能调优建议

为了提升线程池的性能和稳定性,以下是一些实践建议:

  • 合理设置 corePoolSize 与 maximumPoolSize:依据系统核心数、任务类型(CPU 密集型/IO 密集型)评估线程数量。
  • 选择合适的工作队列:如 LinkedBlockingQueue 适合任务量较大、执行速度快的场景,SynchronousQueue 适合任务快速切换的场景。
  • 线程命名有助于排查问题:通过自定义 ThreadFactory 指定线程名称格式,便于日志跟踪和调试。
  • 监控线程池运行状态:可定期获取线程池的活跃线程数、队列长度等指标。
  • 合理选择拒绝策略:避免任务丢失或线程阻塞,根据系统重要性和容错性决定。
  • 避免线程池泄露:使用后及时 shutdown()shutdownNow(),或使用 try-with-resources 结合封装类。

通过使用线程池,Spring 能确保多个定时任务并发、安全、高效地运行。

通过上述各种方式和同步机制,开发者可以根据不同场景构建出高效、安全的并发程序。


十、拓展

一、new Thread() 有什么问题?

当你每次都用 new Thread() 来创建线程时,可能会遇到这些问题:

1. 资源浪费
  • 每次创建新线程都会消耗大量系统资源(内存+CPU)。
  • 如果并发量大(比如几百上千),频繁创建销毁线程会导致 频繁GC上下文切换开销,影响性能。
2. 缺乏线程复用
  • new Thread() 启动后执行一次就终止了,不能重复使用,每次都要重新 new。
  • 而线程池的线程是 可复用的,可以处理多个任务。
3. 难以管理线程数量
  • 系统中线程太多会导致 OOM(内存溢出)或系统崩溃。
  • new Thread() 没有控制机制,一不小心就开太多。
4. 无法监控和扩展
  • 无法获取任务执行状态(除非你手动管理)。
  • 不支持任务队列、拒绝策略、优雅关闭等线程池特性。

二、线程池的优势

✅ 1. 线程复用
  • 重用已创建的线程,减少创建和销毁的开销。
✅ 2. 任务排队机制
  • 使用任务队列(比如 LinkedBlockingQueue)来缓冲任务。
✅ 3. 线程数量可控
  • 核心线程数、最大线程数、任务队列大小都可配置,防止系统资源耗尽
✅ 4. 支持钩子和扩展
  • 支持生命周期管理、拒绝策略、任务执行时间统计等高级功能。
✅ 5. 更适合高并发生产环境
  • 性能稳定、安全、可控,是 Spring 和各种中间件推荐的标准做法。

对比总结表格:

特性 new Thread() 线程池(ExecutorService
线程复用 ❌ 每次新建 ✅ 可复用
性能 ❌ 慢,系统开销大 ✅ 更高效
管理线程数量 ❌ 需要手动控制 ✅ 有参数配置自动管理
是否适合高并发 ❌ 线程过多易崩溃 ✅ 有限资源管理,安全稳定
队列、拒绝策略等 ❌ 不支持 ✅ 支持完整控制
监控与扩展能力 ❌ 差 ✅ 强

实战推荐(Spring 项目)

  • ✅ 正式项目中统一使用线程池,比如:

    • ThreadPoolExecutor
    • @Async + 自定义线程池
    • CompletableFuture

十一、Java 多线程实战示例

本节将带你从手动构建线程池,到 Spring 异步注解 @Async 的使用,再深入到 CompletableFuture 实现异步任务并收集结果的完整方案。

1. 手动构建线程池 + 自定义拒绝策略

配置线程池 Bean
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.*;

@Configuration
public class ThreadPoolConfig {

    @Bean("customExecutor")
    public ExecutorService customExecutor() {
        return new ThreadPoolExecutor(
            5, // 核心线程数
            10, // 最大线程数
            60, // 空闲线程最大存活时间
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100), // 阻塞队列
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy() // 拒绝策略:直接抛异常
        );
    }
}
控制器中使用线程池提交任务
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.ExecutorService;

@RestController
public class ThreadPoolController {

    @Autowired
    private ExecutorService customExecutor;

    @PostMapping("/thread")
    public void executeTasks() {
        for (int i = 0; i < 20; i++) {
            int taskId = i;
            customExecutor.submit(() -> {
                System.out.println("线程 " + Thread.currentThread().getName() + " 正在执行任务 " + taskId);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                System.out.println("线程 " + Thread.currentThread().getName() + " 完成任务 " + taskId);
            });
        }
    }
}
⚠️ 常见拒绝策略说明
策略名 描述
AbortPolicy 默认策略,直接抛出异常
CallerRunsPolicy 任务由提交线程自己执行,降低任务提交速度
DiscardPolicy 直接丢弃任务,不抛异常
DiscardOldestPolicy 丢弃队列头部(最旧)任务,尝试执行当前任务

2. 使用 Spring 原生 @Async 实现异步任务

第一步:开启异步支持

在启动类或配置类上添加注解:

import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@EnableAsync
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
配置异步线程池
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@Configuration
public class AsyncConfig {

    @Bean("asyncExecutor")
    public Executor asyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("Async-Thread-");
        executor.initialize();
        return executor;
    }
}
异步任务逻辑类
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class AsyncService {

    @Async("asyncExecutor")
    public void doTask(int taskId) {
        System.out.println("【异步】线程 " + Thread.currentThread().getName() + " 开始任务 " + taskId);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("【异步】线程 " + Thread.currentThread().getName() + " 完成任务 " + taskId);
    }
}
控制器中调用异步任务
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AsyncController {

    @Autowired
    private AsyncService asyncService;

    @PostMapping("/async")
    public void triggerAsyncTasks() {
        for (int i = 0; i < 10; i++) {
            asyncService.doTask(i);
        }
        System.out.println("主线程完成调用!");
    }
}

3. 使用 @Async + CompletableFuture 获取返回结果

适用于
多个异步任务并发执行
收集所有任务结果后统一处理或返回

修改异步任务返回 CompletableFuture
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.concurrent.CompletableFuture;

@Service
public class AsyncService {

    @Async("asyncExecutor")
    public CompletableFuture<String> doTaskWithResult(int taskId) {
        System.out.println("【异步】线程 " + Thread.currentThread().getName() + " 开始任务 " + taskId);
        try {
            Thread.sleep(1000 + taskId * 100); // 模拟任务耗时
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        String result = "任务 " + taskId + " 完成 by " + Thread.currentThread().getName();
        System.out.println(result);
        return CompletableFuture.completedFuture(result);
    }
}
控制器中聚合异步结果
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;

@RestController
public class AsyncResultController {

    @Autowired
    private AsyncService asyncService;

    @GetMapping("/async-results")
    public List<String> getAsyncResults() throws Exception {
        List<CompletableFuture<String>> futures = new ArrayList<>();

        for (int i = 0; i < 5; i++) {
            futures.add(asyncService.doTaskWithResult(i));
        }

        // 等待所有任务完成
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

        // 收集结果
        List<String> results = new ArrayList<>();
        for (CompletableFuture<String> future : futures) {
            results.add(future.get());
        }

        return results;
    }
}
示例响应(访问 /async-results):
[
  "任务 0 完成 by Async-Thread-1",
  "任务 1 完成 by Async-Thread-2",
  "任务 2 完成 by Async-Thread-3",
  "任务 3 完成 by Async-Thread-4",
  "任务 4 完成 by Async-Thread-5"
]

总结推荐

实现方式 适用场景 优势描述
ExecutorService 精细控制线程,适合通用应用 灵活性强,可完全自定义线程池细节
@Async Spring 项目异步执行任务 简洁优雅,易管理,整合 AOP、注解式
@Async + CompletableFuture 异步任务返回结果并聚合 组合灵活,可并发+返回处理并行提升性能

你可能感兴趣的:(Java开发,java,开发语言,多线程,spring,boot)