CompletableFuture使用详解

本文围绕Java中CompletableFuture展开,介绍其应用场景,如描述依赖、聚合关系及并行执行等。还阐述了创建异步操作的方法及线程池使用注意事项,以及获取结果、结果转换、消费、组合和任务交互等操作,包含相关方法的区别和代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言


在实际开发中,有时我们需要并行提交的多个异步任务,往往并不是独立的,很多时候业务逻辑处理存在串行依赖、并行、聚合的关系。CompletableFuture就是java 8 针对这一场景所推出的‘新特性’, 帮助我们更好的执行异步或逻辑关联业务。
CompletableFuture是Future接口的扩展和增强。CompletableFuture实现了Future接 口,并在此基础上进行了丰富地扩展,完美地弥补了Future上述的种种问题。更为重要的是, CompletableFuture实现了对任务的编排能力。借助这项能力,我们可以轻松地组织不同任 务的运行顺序、规则以及方式。从某种程度上说,这项能力是它的核心能力。而在以往,虽然 通过CountDownLatch等工具类也可以实现任务的编排,但需要复杂的逻辑处理,不仅耗费精 力且难以维护。

jdk8 API文档:https://docs.oracle.com/javase/8/docs/api/

应用场景

  • 描述依赖关系:
    1. thenApply() 把前面异步任务的结果,交给后面的Function
    2. thenCompose()用来连接两个有依赖关系的任务,结果由第二个任务返回
  • 描述and聚合关系:
    1. thenCombine:任务合并,有返回值
    2. thenAccepetBoth:两个任务执行完成后,将结果交给thenAccepetBoth消耗, 无返回值
    3. runAfterBoth:两个任务都执行完成后,执行下一步操作(Runnable)
  • 描述or聚合关系:
    1. applyToEither:两个任务谁执行的快,就使用那一个结果,有返回值
    2. acceptEither: 两个任务谁执行的快,就消耗那一个结果,无返回值
    3. runAfterEither: 任意一个任务执行完成,进行下一步操作(Runnable)
  • 并行执行:
    CompletableFuture类自己也提供了anyOf()和allOf()用于支持多个CompletableFuture 并行执行

创建异步操作


CompletableFuture 提供了四个静态方法来创建一个异步操作:

1 public static CompletableFuture<Void> runAsync(Runnable runnable) 
2 public static CompletableFuture<Void> runAsync(Runnable runnable, Executor e xecutor) 
3 public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) 
4 public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Exe cutor executor)

这四个方法区别在于:

  • runAsync 方法以Runnable函数式接口类型为参数,没有返回结果,supplyAsync 方法Supplier函数式接口类型为参数,返回结果类型为U;Supplier 接口的 get() 方法 是有返回值的(会阻塞)
  • 没有指定Executor的方法会使用ForkJoinPool.commonPool() 作为它的线程池执 行异步代码。如果指定线程池,则使用指定的线程池运行
  • 默认情况下 CompletableFuture 会使用公共的 ForkJoinPool 线程池,这个线程池 默认创建的线程数是 CPU 的核数(也可以通过 JVM option:- Djava.util.concurrent.ForkJoinPool.common.parallelism 来设置 ForkJoinPool 线程 池的线程数)。如果所有 CompletableFuture 共享一个线程池,那么一旦有任务执行 一些很慢的 I/O 操作,就会导致线程池中所有线程都阻塞在 I/O 操作上,从而造成线程饥饿,进而影响整个系统的性能。
    所以,要根据不同的业务类型创建不同的线程池,以避免互相干扰
    代码示例:
Runnable runnable = ()> System.out.println("执行无返回结果的异步任务"); 
CompletableFuture.runAsync(runnable); 
CompletableFuture<String> future = CompletableFuture.supplyAsync(()> { System.out.println("执行有返回值的异步任务"); 
try { 
	Thread.sleep(5000); 
} catch (InterruptedException e) { 
	e.printStackTrace(); 
} 
return "Hello World";
});
String result = future.get();
System.out.println(result);

获取结果


join&get
join()和get()方法都是用来获取CompletableFuture异步之后的返回值。join()方法抛出的是 uncheck异常(即未经检查的异常),不会强制开发者抛出。get()方法抛出的是经过检查的异 常,ExecutionException, InterruptedException 需要用户手动处理(抛出或者 try catch)
结果处理
当CompletableFuture的计算结果完成,或者抛出异常的时候,我们可以执行特定的 Action。 主要是下面的方法:

1 public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwa ble> action) 
2 public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super T hrowable> action) 
3 public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super T hrowable> action, Executor executor) 
4 public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn)
  • Action的类型是BiConsumer<? super T,? super Throwable>,它可以处理正常的 计算结果,或者异常情况。
  • 方法不以Async结尾,意味着Action使用相同的线程执行,而Async可能会使用其 它的线程去执行(如果使用相同的线程池,也可能会被同一个线程选中执行)。
  • 这几个方法都会返回CompletableFuture,当Action执行完毕后它的结果返回原始 的CompletableFuture的计算结果或者返回异常

whenComplete&exceptionally

1 CompletableFuture<String> future = CompletableFuture.supplyAsync(()> { 
2 	try { 
3 		TimeUnit.SECONDS.sleep(1); 
4 	} catch (InterruptedException e) { 
5 	} 
6 	if (new Random().nextInt(10) % 2 == 0) { 
7 		int i = 12 / 0; 
8 	} 
9 	System.out.println("执行结束!"); 
10 	return "test"; 
11 }); 
12
13 future.whenComplete(new BiConsumer<String, Throwable>() { 
14 @Override 
15 public void accept(String t, Throwable action) { 
16 	System.out.println(t+" 执行完成!"); 
17 } 
18 }); 
19
20 future.exceptionally(new Function<Throwable, String>() { 
21 @Override 
22 public String apply(Throwable t) { 
23 	System.out.println("执行失败:" + t.getMessage());
24 	return "异常xxxx"; 
25 } 
26 }).join();

结果转换


所谓结果转换,就是将上一段任务的执行结果作为下一阶段任务的入参参与重新计算,产生新 的结果。
thenApply
thenApply 接收一个函数作为参数,使用该函数处理上一个CompletableFuture 调用的结 果,并返回一个具有处理结果的Future对象。

1 public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) 
2 public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn) 
3 public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
1 CompletableFuture<Integer> future = CompletableFuture.supplyAsync(()> { 
2 	int result = 100; 
3 	System.out.println("一阶段:" + result); 
4 	return result; 
5 }).thenApply(number ‐> { 
6 	int result = number * 3; 
7 	System.out.println("二阶段:" + result); 
8 	return result; 
9 }); 
10
11 System.out.println("最终结果:" + future.get());

thenCompose
thenCompose 的参数为一个返回 CompletableFuture 实例的函数,该函数的参数是先前计 算步骤的结果。

1 public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends Co mpletionStage<U>> fn);
2 public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? exten ds CompletionStage<U>> fn) ; 
3 public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? exten ds CompletionStage<U>> fn, Executor executor) ;
1 CompletableFuture<Integer> future = CompletableFuture 
2 	.supplyAsync(new Supplier<Integer>() { 
3 		@Override 
4 		public Integer get() { 
5 			int number = new Random().nextInt(30); 
6 			System.out.println("第一阶段:" + number); 
7 			return number; 
8		} 
9 	}) 
10 .thenCompose(new Function<Integer, CompletionStage<Integer>>() { 
11 	@Override 
12 	public CompletionStage<Integer> apply(Integer param) { 
13 		return CompletableFuture.supplyAsync(new Supplier<Integer>() { 14 			@Override 
15 			public Integer get() { 
16 				int number = param * 2; 
17 				System.out.println("第二阶段:" + number); 
18 				return number; 
19 			} 
20 		}); 
21 	} 
22 }); 
23 System.out.println("最终结果: " + future.get());

thenApply 和 thenCompose的区别

  • thenApply 转换的是泛型中的类型,返回的是同一个CompletableFuture;
  • thenCompose 将内部的 CompletableFuture 调用展开来并使用上一个 CompletableFutre 调用的结果在下一步的 CompletableFuture 调用中进行运算,是生 成一个新的CompletableFuture。

结果消费


与结果处理和结果转换系列函数返回一个新的 CompletableFuture 不同,结果消费系列函数 只对结果执行Action,而不返回新的计算值。
根据对结果的处理方式,结果消费函数又分为:

  • thenAccept系列:对单个结果进行消费
  • thenAcceptBoth系列:对两个结果进行消费
  • thenRun系列:不关心结果,只对结果执行Action

thenAccept
通过观察该系列函数的参数类型可知,它们是函数式接口Consumer,这个接口只有输入,没 有返回值。

1 CompletableFuture<Void> future = CompletableFuture 
2 .supplyAsync(()> { 
3 	int number = new Random().nextInt(10); 
4 	System.out.println("第一阶段:" + number); 
5 	return number; 
6 }).thenAccept(number ‐> 
7 	System.out.println("第二阶段:" + number * 5)); 
8
9 System.out.println("最终结果:" + future.get());

thenAcceptBoth
thenAcceptBoth 函数的作用是,当两个 CompletionStage 都正常完成计算的时候,就会执 行提供的action消费两个异步的结果。

1 CompletableFuture<Integer> futrue1 = CompletableFuture.supplyAsync(new Suppl ier<Integer>() { 
2 	@Override 
3 	public Integer get() { 
4 		int number = new Random().nextInt(3) + 1; 
5 		try { 
6 			TimeUnit.SECONDS.sleep(number); 
7 		} catch (InterruptedException e) { 
8 			e.printStackTrace(); 
9 		} 
10 		System.out.println("第一阶段:" + number); 
11 		return number; 
12 	} 
13 }); 
14
15 CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(new Suppl ier<Integer>() { 
16 	@Override 
17 	public Integer get() { 
18 		int number = new Random().nextInt(3) + 1; 
19 		try { 
20 			TimeUnit.SECONDS.sleep(number); 
21 		} catch (InterruptedException e) { 
22 			e.printStackTrace(); 
23 		}
24 		System.out.println("第二阶段:" + number); 
25 		return number; 
26 	} 
27 }); 
28
29 futrue1.thenAcceptBoth(future2, new BiConsumer<Integer, Integer>() { 30 	@Override 
31 	public void accept(Integer x, Integer y) { 
32 		System.out.println("最终结果:" + (x + y)); 
33 	} 
34 }).join();

thenRun
thenRun 也是对线程任务结果的一种消费函数,与thenAccept不同的是,thenRun 会在上一 阶段 CompletableFuture 计算完成的时候执行一个Runnable,Runnable并不使用该 CompletableFuture 计算的结果。

1 CompletableFuture<Void> future = CompletableFuture.supplyAsync(()> { 2 	int number = new Random().nextInt(10); 
3 	System.out.println("第一阶段:" + number); 
4 	return number; 
5 }).thenRun(()> 
6 	System.out.println("thenRun 执行")); 
7 	System.out.println("最终结果:" + future.get());

第一阶段:2 t
henRun 执行
最终结果:null

结果组合


thenCombine
thenCombine 方法,合并两个线程任务的结果,并进一步处理。

1 CompletableFuture<Integer> future1 = CompletableFuture 
2 	.supplyAsync(new Supplier<Integer>() { 
3 		@Override 
4 		public Integer get() { 
5 			int number = new Random().nextInt(10); 
6 			System.out.println("第一阶段:" + number); 
7 			return number;
8 		} 
9 	}); 
10 CompletableFuture<Integer> future2 = CompletableFuture 
11 	.supplyAsync(new Supplier<Integer>() { 
12 		@Override 
13 		public Integer get() { 
14 			int number = new Random().nextInt(10); 
15 			System.out.println("第二阶段:" + number); 
16 			return number; 
17 		} 
18 	}); 
19 CompletableFuture<Integer> result = future1 
20 	.thenCombine(future2, new BiFunction<Integer, Integer, Integer>() { 21 		@Override 
22 		public Integer apply(Integer x, Integer y) { 
23 			return x + y; 
24 		} 
25 	}); 
26 System.out.println("最终结果:" + result.get());

任务交互


所谓线程交互,是指将两个线程任务获取结果的速度相比较,按一定的规则进行下一步处理
applyToEither
两个线程任务相比较,先获得执行结果的,就对该结果进行下一步的转化操作。

1 CompletableFuture<Integer> future1 = CompletableFuture 
2 	.supplyAsync(new Supplier<Integer>() { 
3 		@Override 
4		public Integer get() { 
5 			int number = new Random().nextInt(10); 
6 			System.out.println("第一阶段start:" + number); 
7 			try { 
8 				TimeUnit.SECONDS.sleep(number);
9 			} catch (InterruptedException e) { 
10 				e.printStackTrace(); 
11 			} 
12 			System.out.println("第一阶段end:" + number); 
13 			return number; 
14 		} 
15 	}); 
16 CompletableFuture<Integer> future2 = CompletableFuture 
17 	.supplyAsync(new Supplier<Integer>() { 
18 		@Override 
19 		public Integer get() { 
20 			int number = new Random().nextInt(10); 
21 			System.out.println("第二阶段start:" + number); 
22 			try { 
23 				TimeUnit.SECONDS.sleep(number); 
24			} catch (InterruptedException e) { 
25 				e.printStackTrace(); 
26 			} 
27 			System.out.println("第二阶段end:" + number); 
28 			return number; 
29 		} 
30 	}); 
31
32 future1.applyToEither(future2, new Function<Integer, Integer>() { 
33 	@Override 
34 	public Integer apply(Integer number) { 
35 		System.out.println("最快结果:" + number); 
36 		return number * 2; 
37 	} 
38 }).join();

第一阶段start:6
第二阶段start:5
第二阶段end:5
最快结果:5

acceptEither
两个线程任务相比较,先获得执行结果的,就对该结果进行下一步的消费操作。

1 CompletableFuture<Integer> future1 = CompletableFuture 
2 	.supplyAsync(new Supplier<Integer>() { 
3 		@Override 
4 		public Integer get() { 
5 			int number = new Random().nextInt(10) + 1; 
6 			try { 
7 				TimeUnit.SECONDS.sleep(number); 
8 			} catch (InterruptedException e) { 
9 				e.printStackTrace(); 
10 			} 
11 			System.out.println("第一阶段:" + number); 1
2 			return number; 
13 		} 
14 	}); 
15
16 CompletableFuture<Integer> future2 = CompletableFuture 
17 	.supplyAsync(new Supplier<Integer>() { 
18 		@Override 
19 		public Integer get() { 
20 			int number = new Random().nextInt(10) + 1; 
21 			try { 
22 				TimeUnit.SECONDS.sleep(number); 
23 			} catch (InterruptedException e) { 
24 				e.printStackTrace(); 
25 			} 
26 			System.out.println("第二阶段:" + number); 
27 			return number; 
28 		} 
29 	}); 
30
31 future1.acceptEither(future2, new Consumer<Integer>() { 
32 	@Override 
33 	public void accept(Integer number) { 
34 		System.out.println("最快结果:" + number); 
35 	} 
36 }).join();

第二阶段:3
最快结果:3

runAfterEither
两个线程任务相比较,有任何一个执行完成,就进行下一步操作,不关心运行结果。

1 CompletableFuture<Integer> future1 = CompletableFuture 
2 	.supplyAsync(new Supplier<Integer>() { 
3 		@Override 
4 		public Integer get() { 
5 			int number = new Random().nextInt(5); 
6 			try { 
7 				TimeUnit.SECONDS.sleep(number); 
8 			} catch (InterruptedException e) { 
9 				e.printStackTrace(); 
10 			} 
11 			System.out.println("第一阶段:" + number); 
12 			return number; 
13 		} 
14 	}); 
15	
16 CompletableFuture<Integer> future2 = CompletableFuture 
17 	.supplyAsync(new Supplier<Integer>() { 
18 		@Override 
19 		public Integer get() { 
20 			int number = new Random().nextInt(5); 
21 			try { 
22 				TimeUnit.SECONDS.sleep(number); 
23 			} catch (InterruptedException e) { 
24 				e.printStackTrace(); 
25 			} 
26 			System.out.println("第二阶段:" + number); 
27 			return number; 
28 		} 
29 	}); 
30
31 future1.runAfterEither(future2, new Runnable() { 
32 	@Override 
33 	public void run() { 
34 		System.out.println("已经有一个任务完成了");
35 	} 
36 }).join();

runAfterBoth
两个线程任务相比较,两个全部执行完成,才进行下一步操作,不关心运行结果。

1 CompletableFuture<Integer> future1 = CompletableFuture 
2 	.supplyAsync(new Supplier<Integer>() { 
3 		@Override 
4 		public Integer get() { 
5 			try { 
6 				TimeUnit.SECONDS.sleep(1); 
7 			} catch (InterruptedException e) { 
8 				e.printStackTrace(); 
9 			} 
10 			System.out.println("第一阶段:1"); 
11 			return 1; 
12 		} 
13 	}); 
14
15 CompletableFuture<Integer> future2 = CompletableFuture 
16 	.supplyAsync(new Supplier<Integer>() { 
17 		@Override 
18 		public Integer get() { 
19 			try { 
20 				TimeUnit.SECONDS.sleep(2); 
21 			} catch (InterruptedException e) { 
22 				e.printStackTrace(); 
23 			} 
24 			System.out.println("第二阶段:2"); 
25 			return 2; 
26 		} 
27 	}); 
28
29 future1.runAfterBoth(future2, new Runnable() { 
30 	@Override 
31 	public void run() { 
32 		System.out.println("上面两个任务都执行完成了。"); 
33 	} 
34 }).get();

第一阶段:1
第二阶段:2
上面两个任务都执行完成了。

anyOf
anyOf 方法的参数是多个给定的 CompletableFuture,当其中的任何一个完成时,方法返回 这个 CompletableFuture。

1 Random random = new Random(); 
2 CompletableFuture<String> future1 = CompletableFuture 
3 	.supplyAsync(()> { 
4 		try { 
5 			TimeUnit.SECONDS.sleep(random.nextInt(5));
6 		} catch (InterruptedException e) { 
7 			e.printStackTrace(); 
8 		} 
9 		return "hello"; 
10 	}); 
11
12 CompletableFuture<String> future2 = CompletableFuture 
13 	.supplyAsync(()> { 
14 		try { 
15 			TimeUnit.SECONDS.sleep(random.nextInt(1)); 
16 		} catch (InterruptedException e) { 
17 			e.printStackTrace(); 
18 		} 
19 		return "world"; 
20 	}); 
21 CompletableFuture<Object> result = CompletableFuture.anyOf(future1, future2); 
22 System.out.println(result.get());

world

allOf
allOf方法用来实现多 CompletableFuture 的同时返回。

1 CompletableFuture<String> future1 = CompletableFuture 
2 	.supplyAsync(()> { 
3 		try { 
4 			TimeUnit.SECONDS.sleep(2); 
5 		} catch (InterruptedException e) { 
6 			e.printStackTrace(); 
7 		} 
8 		System.out.println("future1完成!"); 
9 		return "future1完成!"; 
10 	}); 
11
12 CompletableFuture<String> future2 = CompletableFuture 
13 	.supplyAsync(()> { 
14 		System.out.println("future2完成!"); 
15 		return "future2完成!"; 
16 	}); 
17
18 CompletableFuture<Void> combindFuture = CompletableFuture 
19 	.allOf(future1, future2); 
20 	try { 
21 		combindFuture.get(); 
22 	} catch (InterruptedException e) { 
23 		e.printStackTrace(); 
24 	} catch (ExecutionException e) { 
25 		e.printStackTrace(); 
26 	} 
27 System.out.println("future1: " + future1.isDone() + ",future2: " + future2.isDone());

future2完成!
future1完成!
future1: true,future2: true

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值