异步编程
1.概念
同步编程:线程等待返回
异步编程:
2.显式使用线程和线程池实现
2.1 显式使用
- 1.实现Runnable接口
public class SyncExample {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
// 第一种
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
task1();
} catch (Exception e) {
e.printStackTrace();
}
}
});
// Java8写法
// Thread thread = new Thread( () -> {
// try {
// task1();
// } catch (Exception e) {
// e.printStackTrace();
// }
// }, "SyncExample");
task2();
// 同步等待1任务结束
thread.join();
System.out.println(System.currentTimeMillis() - start);
}
public static void task1(){
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Task1");
}
public static void task2() {
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Task2");
}
}
- 2.实现Thread类重写run方法
public class SyncExample2 {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
Thread thread = new Thread("Example"){
@Override
public void run() {
try {
task1();
} catch (Exception e) {
e.printStackTrace();
}
}
};
task2();
// 同步等待1任务结束
thread.join();
System.out.println(System.currentTimeMillis() - start);
}
public static void task1(){
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Task1");
}
public static void task2() {
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Task2");
}
}
问题:
- 每当执行异步任务时,直接创建一个Thread,线程创建与销毁有开销,并且没有限制线程个数,应用线程池
- Thread执行异步任务没有返回值,需要用Future
- 每次都创建是命令式编程方式,需要声明式编程方法,即告诉程序我们要异步执行,但如何实现应该对我们透明
2.2 线程池实现
- 无返回结果
public class SyncExample3ThreadPool {
/**
* 线程池核心线程个数为当前CPU核数
*/
private final static int AVALIABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();
/**
* 最大线程个数为CPU核数2倍
* 拒绝策略为CallerRunsPolicy,当线程池任务饱和,执行拒绝策略不会丢弃新任务,使用调用线程执行
* 使用命名的线程创建工厂来追溯业务
*/
public static final ThreadPoolExecutor POOL_EXECUTOR = new ThreadPoolExecutor(AVALIABLE_PROCESSORS,
AVALIABLE_PROCESSORS * 2, 1, TimeUnit.MINUTES,
new LinkedBlockingQueue<>(5), new ThreadPoolExecutor.CallerRunsPolicy());
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
POOL_EXECUTOR.execute( () -> {
try {
task1();
} catch (Exception e) {
e.printStackTrace();
}
});
task2();
System.out.println(System.currentTimeMillis() - start);
// 挂起当前线程
Thread.currentThread().join();
}
}
- 带返回值
使用Callable类型任务提交到线程,返回Future对象,以阻塞的方式获取结果
public class SyncExample3Return {
/**
* 线程池核心线程个数为当前CPU核数
*/
private final static int AVALIABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();
/**
* 最大线程个数为CPU核数2倍
* 拒绝策略为CallerRunsPolicy,当线程池任务饱和,执行拒绝策略不会丢弃新任务,使用调用线程执行
* 使用命名的线程创建工厂来追溯业务
*/
public static final ThreadPoolExecutor POOL_EXECUTOR = new ThreadPoolExecutor(AVALIABLE_PROCESSORS,
AVALIABLE_PROCESSORS * 2, 1, TimeUnit.MINUTES,
new LinkedBlockingQueue<>(5), new ThreadPoolExecutor.CallerRunsPolicy());
public static void main(String[] args) throws Exception {
// 必须阻塞线程才能获取结果
Future<?> taskReturn = POOL_EXECUTOR.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return task1();
}
});
// Lambda写法
// Future<?> taskReturn = POOL_EXECUTOR.submit(() -> task1());
System.out.println(taskReturn.get());
}
public static String task1(){
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Task1");
return "Task1";
}
}
3.Future实现
JUC中,提供一些方法来检查计算结果是否已经完成,也提供获取计算结果的方法。
3.1 接口方法
- get: 等待异步计算完成并返回结果
如果当前任务计算未完成会阻塞,直到完成
如果其他线程取消/中断,抛出异常 - 带超时时间的get:
任务没有被计算出来前不会一直阻塞,会等待timeout时间后抛异常 - isDone: 任务是否正常完成
- cancel: 取消任务
已经完成或已经取消,失败
没被执行,不会执行
已经运行,根据mayIntereuptIfRunning决定是否中断,true中断 - isCancelled:是否取消
3.2 FutureTask
可被取消的异步计算任务,实现Future接口
任务可以是Callable和Runnable类型
任务完成后不能重启,除非使用runAndReset方法
弊端:
- 不能清楚地表达多个FutureTask之间关系
- get方法还是会阻塞线程
public class FutureExample {
public static String task1(){
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Task1");
return "Task1";
}
public static void main(String[] args) throws Exception {
// 创建任务
FutureTask<String> futureTask = new FutureTask<String>( () -> {
String res = null;
try {
res = task1();
} catch (Exception e) {
e.printStackTrace();
}
return res;
});
// 显式执行A
Thread thread = new Thread(futureTask, "threadA");
// 线程池执行
// POOL_EXECUTOR.execute(futureTask);
thread.start();;
// 等待A完成
String resA = futureTask.get();
System.out.println(resA);
}
}
3.3 FutureTask类详解
5.CompletableFuture
通过编程方式显式设置计算结果和状态以让任务结束的Future,并且作为CompletionStage(计算阶段),当它的计算完成时触发一个函数
多个线程调用一个CompletableFuture的complete,cancel方式时只有一个会成功
- 显式使用
public class CompletableFutureTest {
private final static int AVALIABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();
public static final ThreadPoolExecutor POOL_EXECUTOR = new ThreadPoolExecutor(AVALIABLE_PROCESSORS,
AVALIABLE_PROCESSORS * 2, 1, TimeUnit.MINUTES,
new LinkedBlockingQueue<>(5), new ThreadPoolExecutor.CallerRunsPolicy());
public static void main(String[] args) throws Exception {
CompletableFuture<String> future = new CompletableFuture<String>();
POOL_EXECUTOR.execute( () -> {
// 模拟计算
try {
Thread.sleep(3000);
} catch (Exception e){
e.printStackTrace();
}
// 设置计算结算到future
future.complete("Hello World");
});
// 等待结果,complete完成后,由get而被阻塞的线程被激活
System.out.println(future.get());
}
}
- 异步计算和结果转换
- 基于runAsync
public class CompletableFutureNoRes {
private final static int AVALIABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();
public static final ThreadPoolExecutor POOL_EXECUTOR = new ThreadPoolExecutor(AVALIABLE_PROCESSORS,
AVALIABLE_PROCESSORS * 2, 1, TimeUnit.MINUTES,
new LinkedBlockingQueue<>(5), new ThreadPoolExecutor.CallerRunsPolicy());
public static void main(String[] args) throws Exception {
runAsync();
}
public static void runAsync() throws Exception {
// 默认情况下 使用JVM唯一的ForkJoinPool.commonPool()线程池执行
CompletableFuture future = CompletableFuture.runAsync(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (Exception e){
e.printStackTrace();
}
System.out.println("end");
}
});
// 使用自己线程池
CompletableFuture futureMy = CompletableFuture.runAsync(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (Exception e){
e.printStackTrace();
}
System.out.println("end");
}
}, POOL_EXECUTOR);
// 无返回值 结果为null
System.out.println(future.get());
}
}
- 有返回值
public class CompletableFutureWithRes {
private static final ThreadPoolExecutor bizPoolExecutor = new ThreadPoolExecutor(8, 8, 1, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(10));
// A完成后激活B 但是B拿不到A的结果
public static void main(String[] args) throws Exception {
// 1.基于supplyAsync 获取线程结果
// 默认commonPool, 参数使用自己线程池
CompletableFuture futureA = CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
// 执行任务
System.out.println("futureA");
return "Hello";
}
}, bizPoolExecutor);
// 2.thenRun A完成后激活B 但是B拿不到A的结果,也无法get获取到结果
CompletableFuture futureB = futureA.thenRun(new Runnable() {
@Override
public void run() {
System.out.println("futureB");
}
});
// 3.thenAccept A完成后激活B B可以拿到A的结果,也无法get获取到结果
CompletableFuture futureC = futureA.thenAccept(new Consumer<String>() {
@Override
public void accept(String o) {
System.out.println("A:" + o);
System.out.println("futureC");
}
});
// 4.thenApply B可以拿到A执行结果,可以获取get执行结果
CompletableFuture futureD = futureA.thenApply(new Function<String, String>() {
@Override
public String apply(String o) {
System.out.println("A:" + o);
return "futureD";
}
});
System.out.println(futureD.get());
// 5.whenComplete 设置回调函数 不阻塞调用线程
futureA.whenComplete(new BiConsumer<String, Throwable>() {
@Override
public void accept(String o, Throwable o2) {
if (o2 == null) {
System.out.println("A:" + o);
} else {
o2.printStackTrace();
}
}
});
// 挂起等待任务执行完毕
Thread.currentThread().join();
}
}
- 多个任务
public class TwoCompletableFuture {
// 多个CompletableFuture进行组合运算
public static void main(String[] args) throws Exception {
String ID = "ID1234";
// 1.基于thenCompose 一个CF执行完毕 执行另一个
CompletableFuture res = taskOne(ID).thenCompose(id -> taskTwo(id));
System.out.println(res.get());
// 2.基于thenCombine 俩个任务都完成后,使用这俩个参数
CompletableFuture res2 = taskOne(ID).thenCombine(taskTwo("234"), (one, two) -> {
return one + " " + two;
});
System.out.println(res2.get());
// 3.基于allOf 多个并发任务都完成
List<CompletableFuture<String>> futureList = new ArrayList<>();
futureList.add(taskOne("1"));
futureList.add(taskOne("2"));
futureList.add(taskOne("3"));
// 转换为一个
CompletableFuture<Void> res3 = CompletableFuture.allOf(futureList.toArray(new CompletableFuture[futureList.size()]));
System.out.println(res3.get());
// 4.基于anyOf 等任一一个完成
List<CompletableFuture<String>> futureList2 = new ArrayList<>();
futureList.add(taskOne("1"));
futureList.add(taskOne("2"));
futureList.add(taskOne("3"));
// 转换为一个
CompletableFuture<Object> res4 = CompletableFuture.anyOf(futureList.toArray(new CompletableFuture[futureList.size()]));
System.out.println(res4.get());
}
public static CompletableFuture<String> taskOne(String id){
return CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
return id;
}
});
}
public static CompletableFuture<String> taskTwo(String id){
return CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
return id + "My";
}
});
}
}
- 异常处理
public class ExceptionCompletableFuture {
public static void main(String[] args) throws Exception {
CompletableFuture<String> future = new CompletableFuture<String>();
new Thread( () -> {
try {
throw new Exception();
} catch (Exception e) {
// 如果没有这个 get会一直阻塞
future.completeExceptionally(e);
}
}).start();
// System.out.println(future.get());
// 出现异常返回默认值
System.out.println(future.exceptionally( t -> "Exception").get());
}
}
- Stream流中使用
public class StreamExample {
public static String myCall(String ip){
System.out.println(ip);
return ip + "Success";
}
public static void main(String[] args) {
try {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add("192.168.0." + i);
}
List<CompletableFuture<String>> futureList = list.stream().map(ip ->
CompletableFuture.supplyAsync(() -> myCall(ip))).collect(Collectors.toList());
futureList.stream().forEach(r -> System.out.println(r));
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.Spring框架中异步
4.1 TaskExecutor
public interface TaskExecutor {
void execute(Runnable task)
}
Spring中的实现:
- SimpleAsyncTaskExecutor:该实现不会复用线程,对于每个请求会新创建一个对应的线程来执行。
它支持的并发限制将阻止任何超过限制的调用,通过setConcurrencyLimit方法来限制并发数,默认不限制并发数 - SyncTaskExecutor:不会异步执行提交的任务,而是同步调用线程来执行,这种实现主要用户没有必要多线程进行处理的情况。
- ConcurrencyTaskExecutor:
- SimpleThreadPollTaskExecutor:
- ThreadPoolTaskExecutor:
- TimerTaskExecutor:
4.2 使用
4.2.1 SpringBoot使用TaskExecutor实现
- 启动类
@EnableAsync
@SpringBootApplication
public class App
{
public static void main( String[] args )
{
SpringApplication.run(App.class);
}
/**
* 核心线程数10:线程池创建时初始化的线程数
* 最大线程数20:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
* 缓冲队列200:用来缓冲执行任务的队列
* 允许线程的空闲时间60秒:超过了核心线程数之外的线程,在空闲时间到达之后会被销毁
* 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
* 线程池对拒绝任务的处理策略:此处采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在execute方法的调用线程中运行被拒绝的任务;如果执行程序已被关闭,则会丢弃该任务
* 设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
* 设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住
*/
@EnableAsync
@Configuration
class TaskPoolConfig{
@Bean("taskExecutor")
public TaskExecutor taskExecutor(){
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
// 核心进程数
taskExecutor.setCorePoolSize(5);
// 最大线程个数
taskExecutor.setMaxPoolSize(10);
// 超过线程个数的线程空闲多久被回收
taskExecutor.setKeepAliveSeconds(60);
// 缓存队列大小
taskExecutor.setQueueCapacity(20);
// 拒绝策略 CallerRunsPolicy:队列满了,且所有线程忙碌时,新任务不再异步执行,而是调用新线程来执行
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor .CallerRunsPolicy());
taskExecutor.setThreadNamePrefix("TaskExecutor-");
// 等任务执行完毕才退出
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
taskExecutor.setAwaitTerminationSeconds(60);
taskExecutor.initialize();
return taskExecutor;
}
}
}
- AsyncExecutorExample
@Service
public class AsyncExecutorExample {
@Resource
private TaskExecutor taskExecutor;
public void task(){
for (int i = 0; i < 6; i++) {
taskExecutor.execute(new MessagePrinter("Message" + i));
}
}
/**
* task任务执行完后不会退出因为不是守护线程,需要显示关闭
* ThreadPoolTaskExecutor
*/
public void shutdown(){
if (taskExecutor instanceof ThreadPoolTaskExecutor){
((ThreadPoolTaskExecutor)taskExecutor).shutdown();
}
}
private class MessagePrinter implements Runnable{
private String message;
public MessagePrinter(String message) {
this.message = message;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ": " + message);
}
}
}
- TestController
@RestController
public class TestController {
@Autowired
private AsyncExecutorExample asyncExecutorExample;
@GetMapping("/test")
@ResponseBody
public void test() {
asyncExecutorExample.task();
asyncExecutorExample.shutdown();
}
}
4.2.2 SpringBoot使用@Async实现
调用线程在含有调用@Async方法时立即返回,实际执行发生在Spring中的TaskExecutor异步处理线程中
- AsyncAnnotationExample
@Service
public class AsyncAnnotationExample {
/**
* 指定线程池
* 返回值必须为Future及其子类
*/
@Async("bizExecutor")
public void task(){
for (int i = 0; i < 6; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
/**
* 有返回值
* @return
*/
@Async
public CompletableFuture<String> taskWithRes(){
CompletableFuture<String> res = new CompletableFuture<String>();
System.out.println(Thread.currentThread().getName() + ": " + "Task");
res.complete("success");
return res;
}
}
- TestController
@GetMapping("/test")
@ResponseBody
public void test() {
try {
// 无返回值
asyncAnnotationExample.task();
// 有返回值
CompletableFuture<String> res = asyncAnnotationExample.taskWithRes();
res.whenComplete(new BiConsumer<String, Throwable>() {
@Override
public void accept(String s, Throwable throwable) {
if (throwable == null) {
System.out.println(s);
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
- MyAsyncUncaughtExceptionHandler
@Component
public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
/**
* 当调用get方法时等待结果会抛出异常,对于void类型 无法被捕获且无法传输,可以使用该类处理该类型
*/
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
ex.printStackTrace();
}
}
4.2.3 @Async原理
5.反应式编程中的异步
5.1 反应式编程
Reactive Programming 涉及数据流和变化传播的异步编程范式。
- 及时响应式:系统及时作出响应
- 回弹性:系统面临失败仍然保持及时响应性
- 弹性:系统在不断变化的工作负载下仍然保持及时响应性
- 消息驱动:依靠异步消息传递在组件之间建立边界,确保松耦合,隔离和位置透明性,边界提供将故障委派为消息投递出去的方法。
5.2 Reactive Streams规范
目的提供一个使用非阻塞回压功能对异步流进行处理的标准
5.3 基于RxJava实现异步
RxJava是Reactive Extensions的Java VM实现,是一个库,用于通过使用可观察序列来编写异步和基于事件的程序
- 依赖
<dependency>
<groupId>io.reactivex.rxjava2</groupId>
<artifactId>rxjava</artifactId>
<version>2.2.10</version>
</dependency>
- 基本使用
public static void main(String[] args) {
List<Person> list = new ArrayList<Person>();
// 过滤与输出
Flowable.fromArray(list.toArray(new Person[0]))
.filter(p -> p.getAge() >= 10)
.map(p -> p.getName())
.subscribe(System.out::println);// 输出
}
RxJava操作运算符不能直接使用Threads或ExectutorServices进行异步处理,需要使用Schedulers来抽象统一API背后的并发调度线程池。
- Schedulers.io:在动态变化的集合上运行类IO或阻塞操作
- Schedulers.computation:在后台运行固定数量的专用线程来计算密集型工作
- Schedulers.single: 顺序和FIFO方式在单个线程上运行
- Schedulers.from(Executor): 将线程池包装到Schedulers中
.observeOn(Scheduler.io()) // 切换到IO线程
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律