多线程——任务分组信息
一、CompletableFuture任务管理助手类
1.1 任务分组信息
private static class TaskGroupInfo { // 用于记录当前线程的名称 private final String groupId; // 记录所有线程总任务数 private final AtomicLong totalTasks; // 记录已完成的线程任务数 private final AtomicLong completedTasks; // 表示这个分组下所有异步任务的句柄 private final List<CompletableFuture<?>> futures; // 任务描述 private final String desc; // 添加锁对象 private final Object lock = new Object(); // ...构造器和get/set... public void incrementCompleted() { completedTasks.incrementAndGet(); } }(1)使用final修饰,使其不能修改。
(2)为什么用AtomicLong,而不用Long?
因为Long指的是变量不可修改,这样的话,就没办法计数了,AtomicLong指的是引用不能改,但是对象内部的值可以原子地改。比如说两个线程可能同时读到 5,各自算出 6,各自写回 6 → 实际完成了 2 个任务,但是计数只涨了 1,这叫丢失更新。而AtomicLong用CPU的CAS指令,先检查“当前的值还是我刚才读到的那个嘛?”,如果不是就重试。
(3)什么是“所有一步任务的句柄”?为什么需要存?
CompletableFuture创建出来之后,如果不留个引用抓住它,我们后面就再也找不到它了,这就是“任务句柄”的意义。使用<?>表示它是泛型通配符。表示“我不关心这个future完成后返回什么类型”。
(4)为什么专门 new 一个空 Object 当锁?
避免任何拿到这个实例引用的外部代码都能把我的锁抢走导致死锁或者性能问题,这里用一个私有的、不外泄的Object专门当锁,这样只有本类内部的代码能竞争这把锁。
(5)incrementCompleted()是什么?
incrementCompleted() = “原子地+1并返回新值”。这个方法会被很多线程同时调用,每个CompletableFuture完成时,执行它的那个线程都回来调一次。
ps: 从公司里大佬们写的代码看的,可能没有很规范