countdownlatch 使用给定的计数值(count)初始化。await 方法会阻塞直到当前的计数值(count)由于 countdown 方法的调用达到 0,count 为 0 之后所有等待的线程都会被释放,并且随后对await方法的调用都会立即返回。
构造方法:
//参数count为计数值public countdownlatch(int count) {};
常用方法// 调用 await() 方法的线程会被挂起,它会等待直到 count 值为 0 才继续执行public void await() throws interruptedexception {}; // 和 await() 类似,若等待 timeout 时长后,count 值还是没有变为 0,不再等待,继续执行public boolean await(long timeout, timeunit unit) throws interruptedexception {}; // 会将 count 减 1,直至为 0public 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 main(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.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(); }
以上就是如何在java中使用多线程来防止主线程提前结束?的详细内容。