java 异步
一个CPU核心在同一时刻只能运行一个线程。这是因为每个核心有一套寄存器和执行单元,只能处理一个任务。
一.小测试
/*** 测试异步*/
public class TestThread {public static class AsyncThread extends Thread{@Overridepublic void run(){System.out.println("当前线程名称:" + this.getName() + "," +" 执行线程名称:" + Thread.currentThread().getName() + "-hello");}}public static void main(String[] args) {AsyncThread asyncThread = new AsyncThread();asyncThread.start();AsyncThread asyncThread2 = new AsyncThread();asyncThread2.start();}
}
执行main后得到
当前线程名称:Thread-1, 执行线程名称:Thread-1-hello
当前线程名称:Thread-0, 执行线程名称:Thread-0-hello
二.线程池
private ExecutorService executor = Executors.newCachedThreadPool();public void Func (){executor.submit(new Runnable() {@Overridepublic void run() {try {Thread.sleep(1000);System.out.println("睡一秒");System.out.println("执行线程名称:" + Thread.currentThread().getName() + "-hello");} catch (InterruptedException e) {throw new RuntimeException(e);}}});}/*** 关闭线程池*/public void shutdown(){executor.shutdown();}public static void main(String[] args) {TestThread testThread = new TestThread();testThread.Func();testThread.Func();testThread.Func();testThread.shutdown();}
执行main后得到
睡一秒
睡一秒
睡一秒
执行线程名称:pool-1-thread-3-hello
执行线程名称:pool-1-thread-1-hello
执行线程名称:pool-1-thread-2-hello
增添了shutdown()
方法,用于关闭线程池。
三.Future 获取结果异步
import java.util.concurrent.*;/*** future 异步*/
public class FutureTest {public static void main(String[] args) throws Exception {FutureTest futureTest = new FutureTest();futureTest.futureTest();}public void futureTest() throws Exception{ExecutorService executor = Executors.newFixedThreadPool(1);Future<Integer> future = executor.submit(()->{System.out.println("futureTest");Thread.sleep(5000);return 3;});// 方案1:立即获取结果,阻塞主线程// 方案2:设定超时时间获取结果// 方案3:不获取结果,让任务异步执行System.out.println("main函数执行结束: ");}
}
- 创建了一个固定大小为 1 的线程池。
- 往线程池提交了一个会返回整数结果的异步任务
- 调用
System.in.read()
让主线程阻塞,防止程序退出。
3.1 方案1:立即获取结果,阻塞主线程
try {
Integer integer = future.get();
System.out.println("integer = " + integer);
}catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}finally {
executor.shutdown();
}
执行main后得到
futureTest
integer = 3
main函数执行结束:
3.2 方案2:设定超时时间获取结果
try {
Integer result = future.get(3, TimeUnit.SECONDS);
System.out.println("任务结果: " + result);
} catch (TimeoutException e) {
System.err.println("任务执行超时");
future.cancel(true);
} catch (InterruptedException | ExecutionException e) {
System.err.println("任务执行异常: " + e.getMessage());
Thread.currentThread().interrupt();
} finally {
executor.shutdown();
}
执行main后得到
futureTest
任务执行超时
main函数执行结束:
3.3 方案3:不获取结果,让任务异步执行
executor.shutdown();
if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
执行main后得到
futureTest
main函数执行结束:
把线程池的关闭操作放在finally
块中执行,确保资源能够被正确释放
- 方案 1:直接调用
future.get()
获取结果,同时处理可能出现的异常。 - 方案 2:使用带超时时间的
future.get(timeout, unit)
方法获取结果,防止长时间阻塞。 - 方案 3:让任务完全异步执行,通过
awaitTermination
等待所有任务完成后再关闭线程池。
四.CompletableFuture
异步非阻塞
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;/*** CompletableFuture 异步非阻塞*/
public class CompletableFutureExample {public static void main(String[] args) {int number = 10;CompletableFuture<Long> future = CompletableFuture.supplyAsync(() -> factorial(number));try {// 方法1:使用 get() 阻塞(不推荐单独使用)
// long result = future.get();long result = future.get(5, TimeUnit.MINUTES);System.out.println("Result: " + result);// 方法3:非阻塞方式 - 计算完成时自动触发回调future.thenAccept(res ->System.out.println("异步计算完成,结果: " + res));// 方法4:异常处理future.exceptionally(ex -> {System.err.println("计算过程中发生异常: " + ex.getMessage());return -1L;});} catch (InterruptedException e) {Thread.currentThread().interrupt();System.err.println("线程被中断: " + e.getMessage());} catch (ExecutionException e) {System.err.println("计算异常: " + e.getCause());} catch (TimeoutException e) {System.err.println("计算超时,取消任务");future.cancel(true);}}private static long factorial(int n) {long result = 1;for (int i = 1; i <= n; i++) {result += i;}return result;}}
五.SpringBoot配置异步池
package com.atproject.common.utils;import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.ControllerAdvice;
import java.util.concurrent.ThreadPoolExecutor;/*** 线程池配置*/
@ControllerAdvice
@Slf4j
public class ThreadPoolConfiguration {/*** 线程池配置* @return*/@Primary@Bean(name = "threadPoolTaskExecutor", destroyMethod = "shutdown")public ThreadPoolTaskExecutor threadPoolTaskExecutor(){ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5);executor.setMaxPoolSize(20);executor.setQueueCapacity(200);executor.setKeepAliveSeconds(300);executor.setThreadNamePrefix("custom-executor-");executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}
}
5.1 方法上使用注解
package com.atproject.threadPool;import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.extensions.Extension;
import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.*;
import java.util.concurrent.CompletableFuture;@RestController
@Tag(name = "测试异步", description = "测试异步", extensions = {@Extension(properties = {@ExtensionProperty(name = "x-order", value = "400", parseValue = true)})
})
@RequestMapping("threadPool")
public class ThreadPoolController {/*** 调用异步接口* @return 结果*/@Operation(summary = "调用异步接口", extensions = {@Extension(properties = {@ExtensionProperty(name = "x-order", value = "1", parseValue = true)})})@GetMapping("invokingFunction")@Async("threadPoolTaskExecutor")public CompletableFuture<Boolean> execute(Integer num) {System.out.println("线程:" + Thread.currentThread().getName() + " , 任务:" + num);return CompletableFuture.completedFuture(true);}
}