目标是去优化一个对账系统,对账系统需要先去查出俩个数据集合再去比对,入库。
- 之前是一个线程去串行,因为来个数据集合没有顺序相关,所以开俩个线程,每个线程去查询一个集合,使用join去等待执行完成。
- 但是会出现重复的创建多个线程,所以使用线程池,但文中说使用线程池没有办法去阻塞的等待任务执行完成(可以使用submit获取到future啊!!🤣🤣)
- 使用countDownLatch来做线程阻塞计数器。 ```java
// 创建2个线程的线程池 Executor executor = Executors.newFixedThreadPool(2); while(存在未对账订单){ // 计数器初始化为2 CountDownLatch latch = new CountDownLatch(2); // 查询未对账订单 executor.execute(()-> { pos = getPOrders(); latch.countDown(); }); // 查询派送单 executor.execute(()-> { dos = getDOrders(); latch.countDown(); });
// 等待两个查询操作结束 latch.await();
// 执行对账操作 diff = check(pos, dos); // 差异写入差异库 save(diff); } ```
- 但是对比和入库还是串行 因为查询数据和对比是生产者和消费者的关系,所以使用双队列去保存俩个数据集合,但是要保持步调一致,所以使用CyclicBarrier来做线程同步操作
CountDownLatch和CyclicBarrier区别
- CountDownLatch 主要用来解决一个线程等待多个线程的场景,可以类比旅游团团长要等待所有的游客到齐才能去下一个景点;而 CyclicBarrier 是一组线程之间互相等待,更像是几个驴友之间不离不弃。
- CountDownLatch 的计数器是不能循环利用的,也就是说一旦计数器减到 0,再有线程调用 await(),该线程会直接通过。但 CyclicBarrier 的计数器是可以循环利用的,而且具备自动重置的功能,一旦计数器减到 0 会自动重置到你设置的初始值。除此之外,CyclicBarrier 还可以设置回调函数,可以说是功能丰富。
课后思考
本章最后的示例代码中,CyclicBarrier 的回调函数我们使用了一个固定大小的线程池,你觉得是否有必要呢?
回答
多个线程会导致多个线程去执行比对 查询集合A和查询集合B的顺序不一致,带来数据不一致了。