Java常用异步方式总结

使用建议

完整代码见https://gitee.com/pinetree-cpu/parent-demon

提供了postMan调试json文件于security-demo/src/main/resources/test_file/java-async.postman_collection.json

可导入postMan中进行调试

Java异步方式以及使用场景

继承Thread类

新建三个类继承Thread,以其中一个ExtThread01为例

java 复制代码
@Slf4j
public class ExtThread01 extends Thread {
    public ExtThread01(String name) {
        super(name);
    }

    @Override
    public void run() {
        log.info("execute extThread01 start {}", Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("execute extThread01 end {}", Thread.currentThread().getName());
    }
}

抽取测试方法

java 复制代码
    private static void executeExtendThread() throws InterruptedException {
        ExtThread01 extThread01 = new ExtThread01("extThread01");
        ExtThread02 extThread02 = new ExtThread02("extThread02");
        ExtThread03 extThread03 = new ExtThread03("extThread03");
        extThread01.start();
        extThread02.start();
        extThread03.start();
        executeMainThread();
    }

    private static void executeMainThread() throws InterruptedException {
        log.info("main thread start {}", Thread.currentThread().getName());
        Thread.sleep(3000);
        log.info("main thread end {}", Thread.currentThread().getName());
    }

输出结果

bash 复制代码
2025-03-29 17:29:00.181  INFO 11864 --- [nio-8888-exec-3] c.t.s.service.impl.TestServiceImpl       : main thread start http-nio-8888-exec-3
2025-03-29 17:29:00.182  INFO 11864 --- [    extThread01] c.t.s.entity.thread.ExtThread01          : execute extThread01 start extThread01
2025-03-29 17:29:00.182  INFO 11864 --- [    extThread03] c.t.s.entity.thread.ExtThread03          : execute extThread03 start extThread03
2025-03-29 17:29:00.182  INFO 11864 --- [    extThread02] c.t.s.entity.thread.ExtThread02          : execute extThread02 start extThread02
2025-03-29 17:29:03.186  INFO 11864 --- [    extThread02] c.t.s.entity.thread.ExtThread02          : execute extThread02 end extThread02
2025-03-29 17:29:03.186  INFO 11864 --- [nio-8888-exec-3] c.t.s.service.impl.TestServiceImpl       : main thread end http-nio-8888-exec-3
2025-03-29 17:29:03.186  INFO 11864 --- [    extThread01] c.t.s.entity.thread.ExtThread01          : execute extThread01 end extThread01
2025-03-29 17:29:03.186  INFO 11864 --- [    extThread03] c.t.s.entity.thread.ExtThread03          : execute extThread03 end extThread03

实现Runnable接口

相对于继承Thread,实现Runnable可以通过继承来更好的实现逻辑复用,如下新建抽象类,在run()中封装特定的业务操作

java 复制代码
/**
 * @author PineTree
 * @description: 抽象业务runnable
 * @date 2025/3/2 17:28
 */
@Slf4j
public abstract class AbstractBizRunnable implements Runnable {
    @Override
    public void run() {
        log.info("执行特定业务的通用操作");
        try {
            start();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    protected abstract void start() throws InterruptedException;
}

新建三个子类继承AbstractBizRunnable ,如下以为例Biz01Runnable

java 复制代码
/**
 * @author PineTree
 * @description: 业务01runnable
 * @date 2025/3/2 17:50
 */
@Slf4j
public class Biz01Runnable extends AbstractBizRunnable {

    @Override
    protected void start() throws InterruptedException {
        log.info("execute biz01Runnable start {}", Thread.currentThread().getName());
        Thread.sleep(3000);
        log.info("execute biz01Runnable end {}", Thread.currentThread().getName());
    }
}

抽取测试方法

java 复制代码
    private void executeImplRunnable(AsyncVO asyncVO) throws InterruptedException {
        // 使用线程池
        if (asyncVO.isUsePoolFlag()) {
            simplePoolExecute.execute(new Biz01Runnable());
            simplePoolExecute.execute(new Biz02Runnable());
            simplePoolExecute.execute(new Biz03Runnable());
        } else { // 手动调用
            Thread biz1thread = new Thread(new Biz01Runnable(), "biz01Runnable");
            Thread biz2thread = new Thread(new Biz02Runnable(), "biz02Runnable");
            Thread biz3thread = new Thread(new Biz03Runnable(), "biz03Runnable");
            biz1thread.start();
            biz2thread.start();
            biz3thread.start();
        }
        executeMainThread();
    }

输出结果,可以看到在每一个线程中都执行了特定业务的通用操作

bash 复制代码
2025-03-29 17:36:42.202  INFO 11864 --- [nio-8888-exec-8] c.t.s.service.impl.TestServiceImpl       : main thread start http-nio-8888-exec-8
2025-03-29 17:36:42.202  INFO 11864 --- [  biz01Runnable] c.t.s.e.runnable.AbstractBizRunnable     : 执行特定业务的通用操作
2025-03-29 17:36:42.202  INFO 11864 --- [  biz02Runnable] c.t.s.e.runnable.AbstractBizRunnable     : 执行特定业务的通用操作
2025-03-29 17:36:42.202  INFO 11864 --- [  biz01Runnable] c.t.s.entity.runnable.Biz01Runnable      : execute biz01Runnable start biz01Runnable
2025-03-29 17:36:42.203  INFO 11864 --- [  biz02Runnable] c.t.s.entity.runnable.Biz02Runnable      : execute biz02Runnable start biz02Runnable
2025-03-29 17:36:42.203  INFO 11864 --- [  biz03Runnable] c.t.s.e.runnable.AbstractBizRunnable     : 执行特定业务的通用操作
2025-03-29 17:36:42.203  INFO 11864 --- [  biz03Runnable] c.t.s.entity.runnable.Biz03Runnable      : execute biz03Runnable start biz03Runnable
2025-03-29 17:36:45.206  INFO 11864 --- [  biz03Runnable] c.t.s.entity.runnable.Biz03Runnable      : execute biz03Runnable end biz03Runnable
2025-03-29 17:36:45.206  INFO 11864 --- [  biz02Runnable] c.t.s.entity.runnable.Biz02Runnable      : execute biz02Runnable end biz02Runnable
2025-03-29 17:36:45.206  INFO 11864 --- [nio-8888-exec-8] c.t.s.service.impl.TestServiceImpl       : main thread end http-nio-8888-exec-8
2025-03-29 17:36:45.206  INFO 11864 --- [  biz01Runnable] c.t.s.entity.runnable.Biz01Runnable      : execute biz01Runnable end biz01Runnable

使用CallableFutureTask

抽取测试方法

java 复制代码
    private void executeCallableFuture() throws InterruptedException {
        List<CompletableFuture<String>> allFutures = new ArrayList<>();
        allFutures.add(CompletableFuture.supplyAsync(() -> {
            log.info("CALLABLE_FUTURE task 01 start");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {}
            log.info("CALLABLE_FUTURE task 01 end");
            return "task01 result";
        }, simplePoolExecute));
        allFutures.add(CompletableFuture.supplyAsync(() -> {
            log.info("CALLABLE_FUTURE task 02 start");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {}
            log.info("CALLABLE_FUTURE task 02 end");
            return "task02 result";
        }, simplePoolExecute));
        allFutures.add(CompletableFuture.supplyAsync(() -> {
            log.info("CALLABLE_FUTURE task 02 start");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {}
            log.info("CALLABLE_FUTURE task 02 end");
            return "task03 result";
        }, simplePoolExecute));
        CompletableFuture.allOf(allFutures.toArray(new CompletableFuture[0]));
        executeMainThread();
    }

测试结果

bash 复制代码
2025-03-29 17:46:01.572  INFO 11864 --- [ingThreadPool-2] c.t.s.service.impl.TestServiceImpl       : CALLABLE_FUTURE task 02 start
2025-03-29 17:46:01.572  INFO 11864 --- [ingThreadPool-3] c.t.s.service.impl.TestServiceImpl       : CALLABLE_FUTURE task 02 start
2025-03-29 17:46:01.572  INFO 11864 --- [nio-8888-exec-2] c.t.s.service.impl.TestServiceImpl       : main thread start http-nio-8888-exec-2
2025-03-29 17:46:01.572  INFO 11864 --- [ingThreadPool-1] c.t.s.service.impl.TestServiceImpl       : CALLABLE_FUTURE task 01 start
2025-03-29 17:46:04.572  INFO 11864 --- [ingThreadPool-2] c.t.s.service.impl.TestServiceImpl       : CALLABLE_FUTURE task 02 end
2025-03-29 17:46:04.572  INFO 11864 --- [nio-8888-exec-2] c.t.s.service.impl.TestServiceImpl       : main thread end http-nio-8888-exec-2
2025-03-29 17:46:04.572  INFO 11864 --- [ingThreadPool-3] c.t.s.service.impl.TestServiceImpl       : CALLABLE_FUTURE task 02 end
2025-03-29 17:46:04.588  INFO 11864 --- [ingThreadPool-1] c.t.s.service.impl.TestServiceImpl       : CALLABLE_FUTURE task 01 end

使用Spring异步方法

定义异步类

java 复制代码
@Slf4j
@Service
public class AsyncService {
    @Async
    public CompletableFuture<String> asyncMethodWithReturn() {
        try {
            // 模拟耗时操作
            Thread.sleep(3000);
            log.info("带返回值的异步方法执行完成 - {}", Thread.currentThread().getName());
            return CompletableFuture.completedFuture("Hello Async Good Result");
        } catch (InterruptedException e) {
            log.error("asyncMethodWithReturn-error", e);
            return CompletableFuture.completedFuture(e.getMessage());
        }
    }
}

启动类添加@EnableAsync 注解

java 复制代码
@SpringBootApplication
@MapperScan("com.tgh.securitydemo.mapper")
@EnableAsync
public class SecurityDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SecurityDemoApplication.class, args);
    }

}

抽取测试方法

java 复制代码
    private void executeSpringAsync() {
        asyncService.asyncMethodWithReturn().whenCompleteAsync((result, ext) -> {
            log.info("async方法执行完毕后获取返回结果-{}", result);
        });
        int result = 1 + 1;
        log.info("调用 async方法后发完消息后,让我们看看 1 + 1 的结果吧:{}", result);
    }

测试结果

bash 复制代码
2025-03-29 17:52:54.012  INFO 11864 --- [nio-8888-exec-5] c.t.s.service.impl.TestServiceImpl       : 调用 async方法后发完消息后,让我们看看 1 + 1 的结果吧:2
2025-03-29 17:52:57.031  INFO 11864 --- [cTaskExecutor-1] c.tgh.securitydemo.service.AsyncService  : 带返回值的异步方法执行完成 - SimpleAsyncTaskExecutor-1
2025-03-29 17:52:57.034  INFO 11864 --- [nPool-worker-11] c.t.s.service.impl.TestServiceImpl       : async方法执行完毕后获取返回结果-Hello Async Good Result

基于MQ,以rabbitMQ为例

添加mq相关配置

yaml 复制代码
spring:
  rabbitmq:
    host: 192.168.32.155
    port: 5672
    username: admin
    password: 123
    virtual-host: /
java 复制代码
@Configuration
public class RabbitMQConfig {
    // 定义队列名称
    public static final String QUEUE_NAME = "springboot.demo.queue";

    // 创建一个队列
    @Bean
    public Queue queue() {
        // 参数说明: name: 队列名称; durable: 是否持久化; exclusive: 是否排他; autoDelete: 是否自动删除
        return new Queue(QUEUE_NAME, true, false, false);
    }
}

新建消费者

java 复制代码
/**
 * @author PineTree
 * @description: 消息消费
 * @date 2025/3/29 14:09
 */
@Component
@Slf4j
public class MessageConsumer {
    /**
     * 监听指定队列的消息
     * @param message 接收到的消息
     */
    @RabbitListener(queues = RabbitMQConfig.QUEUE_NAME)
    public void receiveMessage(String message) {
        log.info("接收到了消息,等一下再消费");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("消费了【{}】", message);
    }
}

封装测试方法

java 复制代码
    private void executeRabbitMQ() {
        String message = "发送了";
        rabbitTemplate.convertAndSend(RabbitMQConfig.QUEUE_NAME, message);
        log.info("消息:【{}】已发送到队列【{}】", message, RabbitMQConfig.QUEUE_NAME);
        int result = 1 + 2;
        log.info("发完消息后,让我们看看 1 + 2 的结果吧:{}", result);
    }

测试结果

bash 复制代码
2025-03-29 17:58:34.934  INFO 11864 --- [nio-8888-exec-4] c.t.s.service.impl.TestServiceImpl       : 消息:【发送了】已发送到队列【springboot.demo.queue】
2025-03-29 17:58:34.934  INFO 11864 --- [nio-8888-exec-4] c.t.s.service.impl.TestServiceImpl       : 发完消息后,让我们看看 1 + 2 的结果吧:3
2025-03-29 17:58:34.972  INFO 11864 --- [ntContainer#0-1] c.t.s.consumer.MessageConsumer           : 接收到了消息,等一下再消费
2025-03-29 17:58:37.979  INFO 11864 --- [ntContainer#0-1] c.t.s.consumer.MessageConsumer           : 消费了【发送了】

总结

对比总结表

实现方式 返回值支持 线程管理 复杂度 适用规模 典型应用场景
继承Thread 手动 简单异步任务
实现Runnable 手动/池 小-中 线程池任务
Callable+Future ✔️ 手动/池 需要结果获取
Spring @Async ✔️ 自动 中-大 Spring应用
消息队列 ✔️(间接) 自动 分布式系统

选择建议

  1. 简单任务:优先考虑Runnable+线程池
  2. 需要结果:使用Callable+Future或CompletableFuture
  3. Spring项目:首选@Async注解
  4. 跨系统/高可靠:采用消息队列
  5. 新项目:推荐CompletableFuture或响应式编程(如Reactor)

演进趋势

现代Java开发中:

  • 直接使用Thread/Runnable的方式逐渐减少
  • CompletableFuture和响应式编程越来越普及
  • 在微服务架构中,消息队列成为跨服务异步的首选
  • Spring生态倾向于使用@Async和消息驱动
相关推荐
无心水1 小时前
【分布式利器:腾讯TSF】10、TSF故障排查与架构评审实战:Java架构师从救火到防火的生产哲学
java·人工智能·分布式·架构·限流·分布式利器·腾讯tsf
Boilermaker19928 小时前
[Java 并发编程] Synchronized 锁升级
java·开发语言
Cherry的跨界思维8 小时前
28、AI测试环境搭建与全栈工具实战:从本地到云平台的完整指南
java·人工智能·vue3·ai测试·ai全栈·测试全栈·ai测试全栈
MM_MS8 小时前
Halcon变量控制类型、数据类型转换、字符串格式化、元组操作
开发语言·人工智能·深度学习·算法·目标检测·计算机视觉·视觉检测
꧁Q༒ོγ꧂9 小时前
LaTeX 语法入门指南
开发语言·latex
njsgcs9 小时前
ue python二次开发启动教程+ 导入fbx到指定文件夹
开发语言·python·unreal engine·ue
alonewolf_999 小时前
JDK17新特性全面解析:从语法革新到模块化革命
java·开发语言·jvm·jdk
一嘴一个橘子9 小时前
spring-aop 的 基础使用(啥是增强类、切点、切面)- 2
java
sheji34169 小时前
【开题答辩全过程】以 中医药文化科普系统为例,包含答辩的问题和答案
java
古城小栈9 小时前
Rust 迭代器产出的引用层数——分水岭
开发语言·rust