Future接口可以创建出新的线程,并在新线程中执行异步操作,但是,如果使用Future接口创建了多个线程,那么这些线程各自执行,不存在依赖关系,并且无法控制各个线程的执行步骤,为了解决这个问题,JDK8开始引入了了Future新的实现类CompletableFuture.
CompletableFuture不但可以创建出异步执行的线程,还可以控制线程的执行步骤,并且可以监控所有线程的结束时刻,例如,可以使用CompletableFuture创建A和B两个线程,然后规定A,B线程各需要执行3个不同的阶段,并且当A,B全部执行完毕后(或任意一个执行完毕后),再触发某个方法
CompletableFuture提供了四个异步执行任务的方法:
[图片上传失败...(imag image-20210628064939775.pnge-6da60a-1624836327362)]
其中,supplyAsync()用于有返回值的任务,runAsync则用于没有返回值的任务,Executor默认使用ForkJoinPool.commonPool()创建的线程池,除了这些用于执行线程任务的方法外,CompletableFuture还提供了多线程执行时的两种逻辑:
allof()方法会一直阻塞,直到线程池Executor中的所有线程全部执行完毕
anyof()方法会一直阻塞,直到线程池Executor中有任何一个线程执行完毕
范例:
有1,2,3,4四个数字,现要求创建4个线程对这些数字进行处理,并要求每个线程必须安装以下步骤执行
-
对4个数字值各自加上64,使之转为A,B,C,D对应的ASCII值
-
将ASCII值转为对应的A,B,C,D字符
-
将转后的A,B,C,D加入一个集合中,
-
当4个线程全部执行完毕后,打印出转换后的结果集
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CompletableFutureDemo {
public static void main(String[] args) {
//原始数据集
CopyOnWriteArrayList<Integer> taskList = new CopyOnWriteArrayList();
taskList.add(1);
taskList.add(2);
taskList.add(3);
taskList.add(4);
// 结果集
List<Character> resultList = new ArrayList<>();
//线程池,可容纳四个线程
ExecutorService executorService = Executors.newFixedThreadPool(4);
CompletableFuture[] cfs = taskList.stream()
//第一阶段
.map(integer -> CompletableFuture.supplyAsync(
() -> calcASCII(integer), executorService)
//第二阶段
.thenApply(i -> {
char c = (char) (i.intValue());
System.out.println("【阶段2】线程"
+ Thread.currentThread().getName() + "执行完毕,"
+ "已将int"
+ i + "转为了字符" + c);
return c;
})
//第三阶段
.whenComplete((ch, e) -> {
resultList.add(ch);
System.out.println("【阶段3】线程" +
Thread.currentThread().getName() + "执行完毕," + "已将"
+ ch + "增加到了结果集" + resultList + "中");
executorService.shutdown();
})
).toArray(CompletableFuture[]::new);
// 封装后无返回值,必须自己whenComplete()获取
CompletableFuture.allOf(cfs).join();//future.get()
System.out.println("完成!result=" + resultList);
}
//计算i的ASCII值
public static Integer calcASCII(Integer i) {
try {
if (i == 1) {
Thread.sleep(5000);
} else {
Thread.sleep(1000);
}
//数字 -> A-D对应的ascii
i = i + 64;
System.out.println("【阶段1】线程" + Thread.currentThread().getName()
+ "执行完毕," + "已将" + i
+ "转为了A(或B或C或D)对应的ASCII" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
return i;
}
}
</pre>
网友评论