在开发中, 一些异步操作会明显加快执行速度带来更好的体验, 但同时也增加了开发的复杂度, 想了用好多线程, 就必须从这些方面去了解
线程的
wait() notify() notifyall()
方法
线程异步返回 Future
ThreadLocal 类
线程池 ThreadPoolExecutor
同步工具类 CountDownLatch,CyclicBarrier,Semaphore,Phaser,Exchanger
估计上面每一个对于 2~3 年的 java 同学来说都是恶梦, 比较难以理解, 本文简单说下 CountDownLatch 和 CyclicBarrier
CountDownLatch
CountDownLatch 一般在执行时间较长的可分解的任务中比较常用, 算是同步工具类中最容易理解的一个.
示例一:
一个简单的例子: 有一个 100 万的 Excel 数据导出, 需要从数据库中查出数据, 并封装成 Excel 数据然后输出到前端.
稍微分析可以知道这个操作肯定会很费时间, 它的瓶颈出在查询数据库数据和写 Excel 上, 如果我每 10 万数据一页读数据库并写 Excel 成一个文件, 最后把所有的 Excel 使用 zip 打包, 使用多线程, 由于读数据库并不会加锁, 性能将会有一个量级的提升 (有实践过), 这时会有一个问题, 我启动多个线程后, 主线程如何才能知道所有的线程都完成了呢, 只有在所有线程都完成了后, 才能对所有 Excel 文件进行打包, 这时可以用 CountDownLatch , 伪代码如下:
- // Excel 线程
- class ExcelThread extend Thread{
- private CountDownLatch countDownLatch;
- public ExcelThread(CountDownLatch countDownLatch){
- this.countDownLatch = countDownLatch;
- }
- public void run(){
- try{
- // do query db & write Excel
- }finally{
- countDownLatch.countDown();
- }
- }
- }
- public static void main(){
- // 假定生成 3 个 Excel
- CountDownLatch countdownlatch = new CountDownLatch(3);
- foreach :
- new ExcelThread(countdownlatch).start();
- countDownLatch.await();
- // do zip compress & down
- }
示例二:
如果用过 IDM 下载工具, 看它的下载进度一定知道它是多线程下载的, 自动分成了多段, 其实用 java 的 RandomAccessFile 配合 CountDownlatch 一样可以做到多线程下载, 可以实例化多个 RandomAccessFile 然后让其指向文件不同的文件位置, 然后向里面填充数据即可, 主线程在分线程全部完成后检验文件完整性.
当然 IDM 做得更好, 它会其它线程都完成了, 如果某一段还卡着的话, 继续分隔, 同样多线程下载, 当然速度就快了.
示例三:
对于 CountDownLatch, 其他线程为游戏玩家, 比如王者荣耀, 主线程为控制游戏开始的线程. 在所有的玩家都准备好之前, 主线程是处于等待状态的, 也就是游戏不能开始. 当所有的玩家准备好之后, 下一步的动作实施者为主线程, 即开始游戏.
CyclicBarrier
相比如 CountDownLatch 是在主线程等待, CyclicBarrier 是子线程相互等待, 而主线程早就已经结束了, 并且在子线程相互等待的同时, 可以附带一个 CountDownLatch 类似功能的线程, 等所有子线程都完成了再操作, 并且 CyclicBarrier 是可重用的, 说这么多, 看它的使用方式就知道什么意思了.
五人六足之类的游戏不知道读者玩过没, 每个玩家是一个线程, 必须互相等待对方准备好才可以进行下一步操作, 其中也可以来一位指挥员他等到所有同学完成准备后下发指令, 左脚, 右脚... 伪代码如下:
- // 游戏玩家线程
- class GamePerson extend Thread{
- private CyclicBarrier cyclicBarrier;
- public GamePerson(CyclicBarrier cyclicBarrier){
- this.cyclicBarrier = cyclicBarrier;
- }
- public void run(){
- // 走下一步的准备阶段
- prepareNextStep();
- cyclicBarrier.await();
- // 走下一步
- nextStep();
- }
- }
- // 主线程
- public static void main(){
- CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
- // 构建 5 个游戏玩家
- foreach:
- new GamePerson(cyclicBarrier);
- // 游戏开始, 并开始计时
- startGame();beginCountTime();
- foreach:gamepersons
- gameperson.start();
- }
- // 主线程, 添加指挥员
- class Leader extend Thread{
- public void run(){
- System.out.println("开始走");
- }
- }
- // 主线程
- public static void main(){
- CyclicBarrier cyclicBarrier = new CyclicBarrier(5,new Leader());
- // 构建 5 个游戏玩家
- foreach:
- new GamePerson(cyclicBarrier);
- // 游戏开始, 并开始计时
- startGame();beginCountTime();
- foreach:gamepersons
- gameperson.start();
- }
- // 前面的例子只能跨一步, 如果需要重用 CyclicBarrier 需要把主线程也当做同步对象, 代码如下
- public static void main(){
- CyclicBarrier cyclicBarrier = new CyclicBarrier(6);
- // 构建 5 个游戏玩家
- foreach:
- new GamePerson(cyclicBarrier);
- // 游戏开始, 并开始计时
- startGame();beginCountTime();
- // 假设让它们走 10 步
- for(int i=0;i<10;i++){
- cyclicBarrier.reset();
- foreach:gamepersons
- gameperson.start();
- cyclicBarrier.await();
- }
- }
一点小推广
创作不易, 希望可以支持下我的开源软件, 及我的小工具, 欢迎来 gitee 点星, fork , 提 bug .
Excel 通用导入导出, 支持 Excel 公式
博客地址:
gitee: https://gitee.com/sanri/sanri-excel-poi
使用模板代码 , 从数据库生成代码 , 及一些项目中经常可以用到的小工具
博客地址:
gitee: https://gitee.com/sanri/sanri-tools-maven
来源: https://www.cnblogs.com/sanri1993/p/11980185.html