开发者

java之使用多线程代替for循环(解决主线程提前结束问题)

开发者 https://www.devze.com 2023-03-10 10:37 出处:网络 作者: 大聪明」
目录CountDownLatch常用方法使用案例应用场景场景1:模拟并发场景2:多个线程完成后,进行汇总合并接下来进入正题总结在使用之前先介绍一个并发需要用到的方法:
目录
  • CountDownLatch
  • 常用方法
  • 使用案例
  • 应用场景
    • 场景1:模拟并发
    • 场景2:多个线程完成后,进行汇总合并
  • 接下来进入正题
    • 总结

      在使用之前先介绍一个并发需要用到的方法:

      CountDownLatch

      • CountDownLatch(也叫闭锁)是一个同步协助类,允许一个或多个线程等待,直到其他线程完成操作集。
      • CountDownLatch 使用给定的计数值(count)初始化。await 方法会阻塞直到当前的计数值(count)由于 countDown 方法的调用达到 0,count 为 0 之后所有等待的线程都会被释放,并且随后对await方js法的调用都会立即lkdwmHSJ返回。

      构造方法:

      //参数count为计数值
      public CountDownLatch(int count) {};  

      常用方法

      // 调用 await() 方法的线程会被挂起,它会等待直到 count 值为 0 才继续执行
      public void await() throws InterruptedException {};
       
      // 和 await() 类似,若等待 timeout 时长后,count 值还是没有变为 0,不再等待,继续执行
      public boolean javascriptawait(long timeout, TimeUnit unit) throws InterruptedException {};
       
      // 会将 count 减 1,直至为 0
      public void countDown() {};

      使用案例

      • 首先是创建实例 CountDownLatch countDown = new CountDownLatch(2);
      • 需要同步的线程执行完之后,计数 -1, countDown.countDown();
      • 需要等待其他线程执行完毕之后,再运行的线程,调用 countDown.await()实编程客栈现阻塞同步。
      • 如下。

      应用场景

      CountDownLatch 一般用作多线程倒计时计数器,强制它们等待其他一组(CountDownLatch的初始化决定)任务执行完成。

      CountDownLatch的两种使用场景:

      • 让多个线程等待,模拟并发。
      • 让单个线程等待,多个线程(任务)完成后,进行汇总合并。

      场景1:模拟并发

      import Java.util.concurrent.CountDownLatch;
       
      /**
       * 让多个线程等待:模拟并发,让并发线程一起执行
       */
      public class CountDownLatchTest {
          public static void main(String[] args) throws InterruptedException {
       
              CountDownLatch countDownLatch = new CountDownLatch(1);
              
              for (int i = 0; i < 5; i++) {
                  new Thread(() -> {
                      try {
                          // 等待
                          countDownLatch.await();
                          String parter = "【" + Thread.currentThread().getName() + "】";
                          System.out.println(parter + "开始执行……");
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }).start();
              }
       
              Thread.sleep(2000);
             
              countDownLatch.countDown();
          }
      }

      场景2:多个线程完成后,进行汇总合并

      很多时候,我们的并发任务,存在前后依赖关系;比如数据详情页需要同时调用多个接口获取数据,并发请求获取到数据后、需要进行结果合并;或者多个数据操作完成后,需要数据 check;这其实都是:在多个线程(任务)完成后,进行汇总合并的场景。

      import java.util.Map;
      import java.util.concurrent.ConcurrentHashMap;
      import java.util.concurrent.CountDownLatch;
       
      /**
       * 让单个线程等待:多个线程(任务)完成后,进行汇总合并
       */
      public class CountDownLatchTest3 {
       
          //用于聚合所有的统计指标
          private static Map map = new ConcurrentHashMap();
          //创建计数器,这里需要统计4个指标
          private static CountDownLatch countDownLatch = new CountDownLatch(4);
       
          public static void maphpin(String[] args) throws Exception {
       
              //记录开始时间
              long startTime = System.currentTimeMillis();
       
              Thread countUserThread = new Thread(() -> {
                  try {
                      System.out.println("正在统计新增用户数量");
                      Thread.sleep(3000);//任务执行需要3秒
                      map.put("userNumber", 100);//保存结果值
                      System.out.println("统计新增用户数量完毕");
                      countDownLatch.countDown();//标记已经完成一个任务
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              });
              Thread countOrderThread = new Thread(() -> {
                  try {
                      System.out.println("正在统计订单数量");
                      Thread.sleep(3000);//任务执行需要3秒
                      map.put("countOrder", 20);//保存结果值
                      System.out.println("统计订单数量完毕");
                      countDownLatch.countDown();//标记已经完成一个任务
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              });
       
              Thread countGoodsThread = new Thread(() -> {
                  try {
                      System.out.println("正在商品销量");
                      Thread.sleep(3000);//任务执行需要3秒
                      map.put("countGoods", 300);//保存结果值
                      System.out.println("统计商品销量完毕");
                      countDownLatch.countDown();//标记已经完成一个任务
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              });
       
              Thread countmoneyThread = new Thread(() -> {
                  try {
                      System.out.开发者_JAVA学习println("正在总销售额");
                      Thread.sleep(3000);//任务执行需要3秒
                      map.put("countMoney", 40000);//保存结果值
                      System.out.println("统计销售额完毕");
                      countDownLatch.countDown();//标记已经完成一个任务
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              });
              
              //启动子线程执行任务
              countUserThread.start();
              countGoodsThread.start();
              countOrderThread.start();
              countmoneyThread.start();
       
              try {
                  //主线程等待所有统计指标执行完毕
                  countDownLatch.await();
                  long endTime = System.currentTimeMillis();//记录结束时间
                  System.out.println("------统计指标全部完成--------");
                  System.out.println("统计结果为:" + map);
                  System.out.println("任务总执行时间为" + (endTime - startTime) + "ms");
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
       
          }
      }

      接下来进入正题

      使用多线程代替for循环提高查询效率,并且防止主线程提前结束导致其他线程数据错误

      直接上代码:

      @Override
          public AppResponse getLocations() throws InterruptedException {
              List<GetLocationVO> vos = new ArrayList<>();
              vos = projectDao.getLocationOne();    
      //      原来的代码
      //        for (GetLocationVO vo : vos) {
      //            List<LocationVO> children = projectDao.getLocationChildren(vo.getId());
      //            vo.setChildren(children);
      //        }
              //改造后的代码
              Thread(vos,10);
              return AppResponse.success("查询成功",vos);
          }
       
          //此处有加锁
          public synchronized void Thread(List<GetLocationVO> list, int nThread) throws InterruptedException {
              if (CollectionUtils.isEmpty(list) || nThread <= 0 || CollectionUtils.isEmpty(list)) {
                  return;
              }
              CountDownLatch latch = new CountDownLatch(list.size());//创建一个计数器(大小为当前数组的大小,确保所有执行完主线程才结束)
              ExecutorService pool = Executors.newFixedThreadPool(nThread);//创建一个固定的线程池
              for (GetLocationVO vo : list) {
                  pool.execute(() -> {
                      //处理的业务
                      List<LocationVO> children = projectDao.getLocationChildren(vo.getId());
                      vo.setChildren(children);
                      latch.countDown();
                  });
              }
              latch.await();
              pool.shutdown();
          }

      总结

      以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

      0

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      关注公众号