java之使用多線程代替for循環(huán)(解決主線程提前結(jié)束問題)
在使用之前先介紹一個(gè)并發(fā)需要用到的方法:
CountDownLatch
CountDownLatch(也叫閉鎖)是一個(gè)同步協(xié)助類,允許一個(gè)或多個(gè)線程等待,直到其他線程完成操作集。CountDownLatch使用給定的計(jì)數(shù)值(count)初始化。await 方法會(huì)阻塞直到當(dāng)前的計(jì)數(shù)值(count)由于 countDown 方法的調(diào)用達(dá)到 0,count 為 0 之后所有等待的線程都會(huì)被釋放,并且隨后對(duì)await方法的調(diào)用都會(huì)立即返回。
構(gòu)造方法:
//參數(shù)count為計(jì)數(shù)值
public CountDownLatch(int count) {}; 常用方法
// 調(diào)用 await() 方法的線程會(huì)被掛起,它會(huì)等待直到 count 值為 0 才繼續(xù)執(zhí)行
public void await() throws InterruptedException {};
// 和 await() 類似,若等待 timeout 時(shí)長(zhǎng)后,count 值還是沒有變?yōu)?0,不再等待,繼續(xù)執(zhí)行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {};
// 會(huì)將 count 減 1,直至為 0
public void countDown() {};使用案例
- 首先是創(chuàng)建實(shí)例 CountDownLatch countDown = new CountDownLatch(2);
- 需要同步的線程執(zhí)行完之后,計(jì)數(shù) -1, countDown.countDown();
- 需要等待其他線程執(zhí)行完畢之后,再運(yùn)行的線程,調(diào)用 countDown.await()實(shí)現(xiàn)阻塞同步。
- 如下。
應(yīng)用場(chǎng)景
CountDownLatch 一般用作多線程倒計(jì)時(shí)計(jì)數(shù)器,強(qiáng)制它們等待其他一組(CountDownLatch的初始化決定)任務(wù)執(zhí)行完成。
CountDownLatch的兩種使用場(chǎng)景:
- 讓多個(gè)線程等待,模擬并發(fā)。
- 讓單個(gè)線程等待,多個(gè)線程(任務(wù))完成后,進(jìn)行匯總合并。
場(chǎng)景1:模擬并發(fā)
import java.util.concurrent.CountDownLatch;
/**
* 讓多個(gè)線程等待:模擬并發(fā),讓并發(fā)線程一起執(zhí)行
*/
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 + "開始執(zhí)行……");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
Thread.sleep(2000);
countDownLatch.countDown();
}
}場(chǎng)景2:多個(gè)線程完成后,進(jìn)行匯總合并
很多時(shí)候,我們的并發(fā)任務(wù),存在前后依賴關(guān)系;比如數(shù)據(jù)詳情頁(yè)需要同時(shí)調(diào)用多個(gè)接口獲取數(shù)據(jù),并發(fā)請(qǐng)求獲取到數(shù)據(jù)后、需要進(jìn)行結(jié)果合并;或者多個(gè)數(shù)據(jù)操作完成后,需要數(shù)據(jù) check;這其實(shí)都是:在多個(gè)線程(任務(wù))完成后,進(jìn)行匯總合并的場(chǎng)景。
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
/**
* 讓單個(gè)線程等待:多個(gè)線程(任務(wù))完成后,進(jìn)行匯總合并
*/
public class CountDownLatchTest3 {
//用于聚合所有的統(tǒng)計(jì)指標(biāo)
private static Map map = new ConcurrentHashMap();
//創(chuàng)建計(jì)數(shù)器,這里需要統(tǒng)計(jì)4個(gè)指標(biāo)
private static CountDownLatch countDownLatch = new CountDownLatch(4);
public static void main(String[] args) throws Exception {
//記錄開始時(shí)間
long startTime = System.currentTimeMillis();
Thread countUserThread = new Thread(() -> {
try {
System.out.println("正在統(tǒng)計(jì)新增用戶數(shù)量");
Thread.sleep(3000);//任務(wù)執(zhí)行需要3秒
map.put("userNumber", 100);//保存結(jié)果值
System.out.println("統(tǒng)計(jì)新增用戶數(shù)量完畢");
countDownLatch.countDown();//標(biāo)記已經(jīng)完成一個(gè)任務(wù)
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread countOrderThread = new Thread(() -> {
try {
System.out.println("正在統(tǒng)計(jì)訂單數(shù)量");
Thread.sleep(3000);//任務(wù)執(zhí)行需要3秒
map.put("countOrder", 20);//保存結(jié)果值
System.out.println("統(tǒng)計(jì)訂單數(shù)量完畢");
countDownLatch.countDown();//標(biāo)記已經(jīng)完成一個(gè)任務(wù)
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread countGoodsThread = new Thread(() -> {
try {
System.out.println("正在商品銷量");
Thread.sleep(3000);//任務(wù)執(zhí)行需要3秒
map.put("countGoods", 300);//保存結(jié)果值
System.out.println("統(tǒng)計(jì)商品銷量完畢");
countDownLatch.countDown();//標(biāo)記已經(jīng)完成一個(gè)任務(wù)
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread countmoneyThread = new Thread(() -> {
try {
System.out.println("正在總銷售額");
Thread.sleep(3000);//任務(wù)執(zhí)行需要3秒
map.put("countMoney", 40000);//保存結(jié)果值
System.out.println("統(tǒng)計(jì)銷售額完畢");
countDownLatch.countDown();//標(biāo)記已經(jīng)完成一個(gè)任務(wù)
} catch (InterruptedException e) {
e.printStackTrace();
}
});
//啟動(dòng)子線程執(zhí)行任務(wù)
countUserThread.start();
countGoodsThread.start();
countOrderThread.start();
countmoneyThread.start();
try {
//主線程等待所有統(tǒng)計(jì)指標(biāo)執(zhí)行完畢
countDownLatch.await();
long endTime = System.currentTimeMillis();//記錄結(jié)束時(shí)間
System.out.println("------統(tǒng)計(jì)指標(biāo)全部完成--------");
System.out.println("統(tǒng)計(jì)結(jié)果為:" + map);
System.out.println("任務(wù)總執(zhí)行時(shí)間為" + (endTime - startTime) + "ms");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}接下來進(jìn)入正題
使用多線程代替for循環(huán)提高查詢效率,并且防止主線程提前結(jié)束導(dǎo)致其他線程數(shù)據(jù)錯(cuò)誤
直接上代碼:
@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());//創(chuàng)建一個(gè)計(jì)數(shù)器(大小為當(dāng)前數(shù)組的大小,確保所有執(zhí)行完主線程才結(jié)束)
ExecutorService pool = Executors.newFixedThreadPool(nThread);//創(chuàng)建一個(gè)固定的線程池
for (GetLocationVO vo : list) {
pool.execute(() -> {
//處理的業(yè)務(wù)
List<LocationVO> children = projectDao.getLocationChildren(vo.getId());
vo.setChildren(children);
latch.countDown();
});
}
latch.await();
pool.shutdown();
}總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
項(xiàng)目依賴Springboot jar失敗解決方案
這篇文章主要介紹了項(xiàng)目依賴Springboot jar失敗解決方案,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08
eclipse實(shí)現(xiàn)可認(rèn)證的DH密鑰交換協(xié)議
這篇文章主要介紹了eclipse實(shí)現(xiàn)可認(rèn)證的DH密鑰交換協(xié)議,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-06-06
Spring的Bean注入解析結(jié)果BeanDefinition詳解
這篇文章主要介紹了Spring的Bean注入解析結(jié)果BeanDefinition詳解,BeanDefinition描述了一個(gè)bean實(shí)例,擁有屬性值、構(gòu)造參數(shù)值和具體實(shí)現(xiàn)的其他信息,其是一個(gè)bean的元數(shù)據(jù),xml中配置的bean元素會(huì)被解析成BeanDefinition對(duì)象,需要的朋友可以參考下2023-12-12
Java數(shù)據(jù)結(jié)構(gòu)之LinkedList從鏈表到實(shí)現(xiàn)
LinkedList是Java中常用的數(shù)據(jù)結(jié)構(gòu)之一,實(shí)現(xiàn)了鏈表的特性,支持快速添加、刪除元素,可以用于實(shí)現(xiàn)隊(duì)列、棧、雙向隊(duì)列等數(shù)據(jù)結(jié)構(gòu)。LinkedList的內(nèi)部實(shí)現(xiàn)采用了雙向鏈表,其中每個(gè)節(jié)點(diǎn)都包含前驅(qū)節(jié)點(diǎn)和后繼節(jié)點(diǎn)的引用,可以直接訪問鏈表的頭尾元素2023-04-04
spring?boot前后端交互之?dāng)?shù)據(jù)格式轉(zhuǎn)換問題
這篇文章主要介紹了spring?boot前后端交互之?dāng)?shù)據(jù)格式轉(zhuǎn)換,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01
springboot 基于Tomcat容器的自啟動(dòng)流程分析
這篇文章主要介紹了springboot 基于Tomcat容器的自啟動(dòng)流程分析,Spring通過注解導(dǎo)入Bean大體可分為四種方式,我們主要來說Import的兩種實(shí)現(xiàn)方法,需要的朋友可以參考下2020-02-02
java根據(jù)當(dāng)前時(shí)間獲取yyyy-MM-dd?HH:mm:ss標(biāo)準(zhǔn)格式的時(shí)間代碼示例
在Java中可以使用java.time包中的LocalDateTime類和DateTimeFormatter類來獲取并格式化當(dāng)前時(shí)間為yyyy-MM-dd?HH:mm:ss的格式,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-10-10
SpringBoot整合SpringBoot-Admin實(shí)現(xiàn)監(jiān)控應(yīng)用功能
本文主要介紹如何整合Spring Boot Admin,以此監(jiān)控Springboot應(yīng)用,文中有相關(guān)的示例代碼供大家參考,需要的朋友可以參考下2023-05-05
Spring MVC簡(jiǎn)介_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
Spring MVC屬于SpringFrameWork的后續(xù)產(chǎn)品,已經(jīng)融合在Spring Web Flow里面。今天先從寫一個(gè)Spring MVC的HelloWorld開始,讓我們看看如何搭建起一個(gè)Spring mvc的環(huán)境并運(yùn)行程序,感興趣的朋友一起學(xué)習(xí)吧2017-08-08
Java異常處理 Throwable實(shí)現(xiàn)方法解析
這篇文章主要介紹了Java異常處理 Throwable實(shí)現(xiàn)方法解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10

