使用建議
完整代碼見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
為例
@Slf4j
public class ExtThread01 extends Thread {public ExtThread01(String name) {super(name);}@Overridepublic 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());}
}
抽取測試方法
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());}
輸出結果
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()
中封裝特定的業務操作
/*** @author PineTree* @description: 抽象業務runnable* @date 2025/3/2 17:28*/
@Slf4j
public abstract class AbstractBizRunnable implements Runnable {@Overridepublic void run() {log.info("執行特定業務的通用操作");try {start();} catch (InterruptedException e) {throw new RuntimeException(e);}}protected abstract void start() throws InterruptedException;
}
新建三個子類繼承AbstractBizRunnable
,如下以為例Biz01Runnable
/*** @author PineTree* @description: 業務01runnable* @date 2025/3/2 17:50*/
@Slf4j
public class Biz01Runnable extends AbstractBizRunnable {@Overrideprotected void start() throws InterruptedException {log.info("execute biz01Runnable start {}", Thread.currentThread().getName());Thread.sleep(3000);log.info("execute biz01Runnable end {}", Thread.currentThread().getName());}
}
抽取測試方法
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();}
輸出結果,可以看到在每一個線程中都執行了特定業務的通用操作
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
抽取測試方法
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();}
測試結果
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異步方法
定義異步類
@Slf4j
@Service
public class AsyncService {@Asyncpublic 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
注解
@SpringBootApplication
@MapperScan("com.tgh.securitydemo.mapper")
@EnableAsync
public class SecurityDemoApplication {public static void main(String[] args) {SpringApplication.run(SecurityDemoApplication.class, args);}}
抽取測試方法
private void executeSpringAsync() {asyncService.asyncMethodWithReturn().whenCompleteAsync((result, ext) -> {log.info("async方法執行完畢后獲取返回結果-{}", result);});int result = 1 + 1;log.info("調用 async方法后發完消息后,讓我們看看 1 + 1 的結果吧:{}", result);}
測試結果
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相關配置
spring:rabbitmq:host: 192.168.32.155port: 5672username: adminpassword: 123virtual-host: /
@Configuration
public class RabbitMQConfig {// 定義隊列名稱public static final String QUEUE_NAME = "springboot.demo.queue";// 創建一個隊列@Beanpublic Queue queue() {// 參數說明: name: 隊列名稱; durable: 是否持久化; exclusive: 是否排他; autoDelete: 是否自動刪除return new Queue(QUEUE_NAME, true, false, false);}
}
新建消費者
/*** @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);}
}
封裝測試方法
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);}
測試結果
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應用 |
消息隊列 | ??(間接) | 自動 | 高 | 大 | 分布式系統 |
選擇建議
- 簡單任務:優先考慮Runnable+線程池
- 需要結果:使用Callable+Future或CompletableFuture
- Spring項目:首選@Async注解
- 跨系統/高可靠:采用消息隊列
- 新項目:推薦CompletableFuture或響應式編程(如Reactor)
演進趨勢
現代Java開發中:
- 直接使用Thread/Runnable的方式逐漸減少
- CompletableFuture和響應式編程越來越普及
- 在微服務架構中,消息隊列成為跨服務異步的首選
- Spring生態傾向于使用@Async和消息驅動