java多線程之CountDownLatch

今天主要簡單說明一下,如何使用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();
        }
    }
}

看一下最終測試的效果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章