今天主要簡單說明一下,如何使用countdownlatch來完成多線程之間的協助操作,看一下具體的業務背景:
開發同事,需要對大量數據進行插入操作,因爲數據量比較大,所以自然而然會想到用到多線程來提高效率,但是問題來了,就是因爲一旦牽涉插入庫操作,勢必會牽涉到事務,本質需求就是,當多個線程進行插入操作,等所有線程執行完畢,一旦發現有一個或多個子線程在執行過車過程中出現了異常,就需要對事務進行全局回滾(也就是之前已經成功執行的線程,都有進行回滾)。其實要清楚這個問題的本質就比較簡單了,那就是拋出異常的線程應該是主線程,回滾也要主線程去執行,下面看一下簡單的代碼實現,其實主要就是用了兩個countdownlatch分別控制主線程和子線程的執行操作以及回滾時機.下面看一下僞代碼
首先定義一個事務回滾類
public class DataRollBack {
public DataRollBack(boolean isRollBack) {
this.isRollBack = isRollBack;
}
//事務是否回滾 true回滾 false不回滾
public boolean isRollBack() {
return isRollBack;
}
public void setRollBack(boolean rollBack) {
isRollBack = rollBack;
}
private boolean isRollBack;
}
再定義一個維護事務管理類,主要包含事務的回滾操作和事務的提交操作
public class TransactionManager {
/**
* 模擬事務回滾
*/
public void rollBack(){
System.out.println("線程:"+Thread.currentThread().getName()+"執行事務回滾");
}
/**
* 模擬事務提交
*/
public void commit(){
System.out.println("線程:"+Thread.currentThread().getName()+"開始提交數據");
}
}
定義一個數據插入業務類,模擬數據插入操作
public class InsertService {
public boolean insert(String data){
System.out.println("開始保存數據到數據庫中");
if(Thread.currentThread().getName().contains("Thread-4"))
return false;
return true;
}
}
定義實現數據插入的業務線程類:
package insert;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
/**
* @author [email protected]
* @version 1.0
* @date 模擬多線程併發插入數據,且如果有一個線程出現了回滾,需要回滾整個線程(已經成功執行的線程需要全部回滾)
* @description
*/
public class DataInsertRunable implements Runnable {
//業務方法
private InsertService insertService;
//主線程
private CountDownLatch mainCountDownLatch;
//子線程
private CountDownLatch childCountDownLatch;
//保存各個子線程執行的結果
private BlockingQueue<Boolean> resultList;
//事務管理
private TransactionManager transactionManager;
//數據結果執行決定是否回滾
private DataRollBack dataRollBack;
public DataInsertRunable(InsertService insertService, CountDownLatch mainCountDownLatch, CountDownLatch childCountDownLatch, BlockingQueue<Boolean> resultList,
TransactionManager transactionManager,DataRollBack dataRollBack) {
this.insertService = insertService;
this.mainCountDownLatch = mainCountDownLatch;
this.childCountDownLatch = childCountDownLatch;
this.resultList = resultList;
this.transactionManager = transactionManager;
this.dataRollBack = dataRollBack;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"子線程開始執行任務");
Boolean result = insertService.insert("test");
resultList.add(result);
//子線程執行
childCountDownLatch.countDown();
//阻塞主線程 回滾事務是由主線程來執行的
try {
mainCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"子線程執行剩下的任務");
if (dataRollBack.isRollBack()){
transactionManager.rollBack();
}
else {
transactionManager.commit();;
}
}
}
寫個主線程測試
package insert;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingDeque;
/**
* @author [email protected]
* @version 1.0
* @date
* @description
*/
public class DataMain {
public static void main(String[] args) {
int CHILD_SUM =5;
CountDownLatch mainCountDownLathc = new CountDownLatch(1);
CountDownLatch childCountDownLathc = new CountDownLatch(CHILD_SUM);
DataRollBack dataRollBack = new DataRollBack(false);
InsertService insertService = new InsertService();
TransactionManager transactionManager = new TransactionManager();
BlockingQueue<Boolean> resultList = new LinkedBlockingDeque<>(10);
//創建5個線程
for (int i=0;i<CHILD_SUM;i++){
DataInsertRunable dataInsertRunable = new DataInsertRunable(insertService,mainCountDownLathc,childCountDownLathc,resultList,transactionManager,dataRollBack);
Thread thread = new Thread(dataInsertRunable);
thread.start();
}
//阻塞子線程 子線程不執行完 下面的邏輯不會被執行
try {
childCountDownLathc.await();
System.out.println("主線程開始執行任務");
//通過隊列獲取結果
for (int i=0;i<CHILD_SUM;i++){
Boolean result = resultList.take();
if (!result){
dataRollBack.setRollBack(true);
}
}
// 執行主線程 此時子線程應該是已經執行完了
mainCountDownLathc.countDown();
if (dataRollBack.isRollBack()){
System.out.println("執行線程出現了異常,需要進行全局事務回滾");
}
else {
System.out.println("多線程插入數據已經全部完畢且提交事務了");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
看一下最終測試的效果