Java组合异步编程(1)

本文介绍了Java中异步编程的异步与同步区别,重点讲解了CompletableFuture如何处理有顺序依赖的异步任务,包括`allOf`和`anyOf`的使用,以及在复杂场景下如依赖多个答案的解决方案。

您好,我是湘王,这是我的优快云博客,欢迎您来,欢迎您再来~

在《计算机干活的两种方式》中我们提到过同步和异步的区别。所谓同步就是事情只能一件接一件地顺着干,而不能跳过。比如外卖小哥送外卖只能一件一件地送,不能说一件先送一半再送另一件。而异步就可以不按顺序出牌,但是这种不按顺序需要以互不影响为前提。比如李雷问韩梅梅一个数学问题,但是韩梅梅一时答不出来,李雷就先去干其他的事情去了。等韩梅梅知道答案的时候,再给李雷打电话告诉他结果。就像这样子:

 

Java也支持这种异步工作模式。还是以代码来举例。

public static void doSomethingElse() {
	try {
		System.out.println("doSomethingElse...");
		Thread.sleep(1_000);
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
}

public static void executeComputation() {
	try {
		System.out.println("executeComputation...");
		Thread.sleep(2_000);
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
}

// 使用Future
public static void testExecutor() throws InterruptedException, ExecutionException {
	// 申请线程池执行
	ExecutorService es = Executors.newSingleThreadExecutor();
	Future<Integer> f = es.submit(() -> {
		System.out.println(Thread.currentThread().getName());
		// 执行线程操作
		executeComputation();
		return 9527;
	});
	// 其他耗时操作
	doSomethingElse();
	System.out.println(f.get());
	es.shutdown();
}

运行代码后,也可以看到异步的执行痕迹。这可以用流程图来表示:

 

代码中的Futrure,就是那个可以通知李雷的「电话」。

那么问题来了,如果李雷有好几个不懂的问题,分别问了韩梅梅、赵梅梅、魏梅梅,而且需要答案之间有先后顺序怎么办呢?(比如要先得到韩梅梅的答案,再拿到赵梅梅的答案才有用)也就是对这种有多个「异步」任务的情况,该如何处理?这就是Java8中CompletableFuture(组合异步编程)要解决的问题。还是用代码来说话。

假如现在有三个异步任务,要求这三个异步任务能够「按顺序」执行,可以试着这么写:

ExecutorService service = Executors.newFixedThreadPool(3);
System.out.println("main start ...");
Future<Integer> f1 = service.submit(() -> {
	System.out.println(Thread.currentThread().getName());
	// 执行线程操作
	executeComputation();
	return 9525;
});
Future<Integer> f2 = service.submit(() -> {
	System.out.println(Thread.currentThread().getName());
	// 执行线程操作
	executeComputation();
	return 9526;
});
Future<Integer> f3 = service.submit(() -> {
	System.out.println(Thread.currentThread().getName());
	// 执行线程操作
	executeComputation();
	return 9527;
});
System.out.println("main end ...");
service.shutdown();

但是运行之后发现,无论如何,「System.out.println("main end ...");」这行代码都不会最后执行,这就达不到「按顺序」执行的要求了。

现在换成CompletableFuture试试看:

ExecutorService service = Executors.newFixedThreadPool(3);
		System.out.println("main start ...");
		CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
			System.out.println("9525...");
			return 9525;
		}, service);

		CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
			System.out.println("9526...");
			return 9526;
		}, service);

		CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(() -> {
			System.out.println("9527...");
			return 9527;
		}, service);

		CompletableFuture<Void> future = CompletableFuture.allOf(future1, future2, future3);
		future.get();
		System.out.println("main end ...");
		service.shutdown();

运行后会发现9525、9526、9527三个任务虽然都是异步任务,但能够「按顺序」排列,满足了最初的要求。

现在,更进一步,如果某个答案取决于两个不相干的问题的共同回答,该怎么实现呢?例如,李雷问韩梅梅,然后又问了赵梅梅,只有在都问她们两个人的情况下,她们才会说出答案,否则不给答案。

// 两个CompletableFuture,全部任务执行完成才返回
public static void testAllOf() throws InterruptedException, ExecutionException {
	CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
	CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "world");

	CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(future1, future2);
	// 返回值是void,不会合并结果
	System.out.println(combinedFuture.get());

	// 需要手动处理每个并行异步任务的结果
	String combined = Stream.of(future1, future2)
							.map(CompletableFuture::join)
							.collect(Collectors.joining(" "));
	// hello world
	System.out.println(combined);
}

这就是神奇的CompletableFuture组合异步编程。可以通过代码让不同的问题强制「同频」,这算不算乱点鸳鸯谱?

当然,如果有同学觉得这很过分,也可以允许「只要有问了她们其中之一就立刻给答案」:

// 两个CompletableFuture,只要有一个任务执行完成就返回
public static void testAnyOf() throws InterruptedException, ExecutionException {
	CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
	CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
		try {
			Thread.sleep(2000);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "world";
	});
	CompletableFuture<Object> combinedFuture = CompletableFuture.anyOf(future1, future2);
	// Hello World
	System.out.println(combinedFuture.get());
}

这更像唐伯虎点秋香中最后揭盖头的场景——只要揭了一个,剩下的全部自己揭开。


感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值