模板方法中的執行緒安全問題

linzm14發表於2023-05-05

1、執行緒安全?

是否存在臨界區,共享的變數,會被不同執行緒寫入

那麼模板方法裡面基類的成員變數或者方法就會存線上程安全問題

2、excel 

 

AbstractExcelSheet

業務資料和excel 邏輯 解耦

讓data 可以 在service 層之間set進來

這樣excel的相關類不用新增到 spring 容器中

 

public abstract class AbstractExcelSheet<T> {
    private XSSFWorkbook workbook;

    private XSSFSheet sheet;

    /**
     * 寫入資料
     */
    private List<T> data;

    public void setData(List<T> data) {
        this.data = data;
    }

    /**
     * 獲取當前類的sheet物件
     *
     * @return sheet物件
     */
    public XSSFSheet getSheet() {
        return sheet;
    }

    /**
     * 設定sheet物件
     *
     * @param sheet sheet物件
     */
    public void setSheet(XSSFSheet sheet) {
        this.sheet = sheet;
    }

    /**
     * 獲取所屬的excel物件
     *
     * @return excel物件
     */
    public XSSFWorkbook getWorkbook() {
        return workbook;
    }

    /**
     * 設定excel物件
     *
     * @param workbook excel物件
     */
    public void setWorkbook(XSSFWorkbook workbook) {
        this.workbook = workbook;
    }

    /**
     * 建立sheet頁基本資訊
     */
    public abstract void createSheet();

    /**
     * 在子類重寫
     *
     * @return 標題列
     */
    public abstract List<String> getTitles();

    /**
     * 生成sheet標題列,此方法不必重寫
     */
    public void createTitleRow() {
        List<String> titles = getTitles();
        // 得到行
        XSSFRow titleRow = getSheet().createRow(0);
        for (int i = 0; i < titles.size(); i++) {
            XSSFCell cell = titleRow.createCell(i);
            cell.setCellStyle(ExportExcelUtil.getTitleStyle(getWorkbook()));
            cell.setCellValue(titles.get(i));
        }
        // 設定行高
        titleRow.setHeight((short) 460);
    }

    /**
     * 新增匯出excel的內容
     *
     * @param data 匯出資料
     */
    public abstract void createDataRow(List<T> data);

    /**
     * writeSheetData
     */
    public void writeSheetData() {
        // 建立表單 sheet
        createSheet();
        // sheet 設定表頭內容(第一行內容)
        createTitleRow();
        // 從資料庫取數並寫到檔案中
        createDataRow(data);
    }

    /**
     * 寫入一行 sheet 資料
     *
     * @param rowIndex sheet 寫入 行索引
     * @param data sheet 實體資料
     * @param enumValues sheet 表頭標題
     * @param <S> 泛型
     */
    protected <S> void writeRowData(int rowIndex, S data, SheetTitleEnum[] enumValues) {
        XSSFRow row = getSheet().createRow(rowIndex);
        int colIndex = 0;
        XSSFCell cell = row.createCell(colIndex);
        for (SheetTitleEnum enumValue : enumValues) {
            String cellValue = ExportExcelUtil.getCellValue(data, enumValue);
            cell.setCellValue(cellValue);
            cell = row.createCell(++colIndex);
        }
    }
}

BusiPackageResultExcelSheet

業務知識包檢查結果.xlsx 業務知識包檢查結果 sheet

 

public class BusiPackageResultExcelSheet<T> extends AbstractExcelSheet<T> {
    private final static String SHEET_NAME = "業務知識包檢查結果";

    @Override
    public void createSheet() {
        this.setSheet(this.getWorkbook().createSheet(SHEET_NAME));
    }

    @Override
    public List<String> getTitles() {
        return BusiPackageResultSheetTitleEnum.titles;
    }

    @Override
    public void createDataRow(List<T> data) {
        // 表頭 rowIndex = 0, 所以 rowIndex = 1
        int rowIndex = 1;
        SheetTitleEnum[] enumValues = BusiPackageResultSheetTitleEnum.values();
        for (T dataEntry : data) {
            // 寫入行資料
            writeRowData(rowIndex++, dataEntry, enumValues);
        }
    }
}

BusiPkgRuleResultExcelSheet

業務知識包檢查結果.xlsx 規則檢查結果 sheet
public class BusiPkgRuleResultExcelSheet<T> extends RuleResultExcelSheet<T> {
    @Override
    public void createDataRow(List<T> data) {
        // 表頭 rowIndex = 0, 所以 rowIndex = 1
        int rowIndex = 1;
        for (T dataEntry : data) {
            // 寫入行資料
            writeRowData(rowIndex++, dataEntry, RuleResultSheetTitleEnum.values());
        }
    }
}

 

RuleResultExcelSheet

規則檢查結果.xlsx 規則檢查結果 sheet
public class RuleResultExcelSheet<T> extends AbstractExcelSheet<T> {
    private final static String SHEET_NAME = "規則檢查結果";

    @Override
    public void createSheet() {
        this.setSheet(this.getWorkbook().createSheet(SHEET_NAME));
    }

    /**
     * 表頭內容
     *
     * @return 表頭內容
     */
    @Override
    public List<String> getTitles() {
        return RuleResultSheetTitleEnum.titles;
    }

    @Override
    public void createDataRow(List<T> data) {
        // 表頭 rowIndex = 0, 所以 rowIndex = 1
        int rowIndex = 1;
        SheetTitleEnum[] enumValues = RuleResultSheetTitleEnum.values();
        for (T dataEntry : data) {
            // 寫入行資料
            writeRowData(rowIndex++, dataEntry, enumValues);
        }
    }
}

上面的設計 大部分的方法都是在基類實現了

當excel的表頭不一樣,是在子類實現的

3、執行緒安全問題點

所有的成員變數都有可能被其他執行緒爭搶,

比如 執行緒1 setData data1 

但是此時如果執行緒1 excel 還沒匯出成功,

執行緒2進來了 setData data2

把執行緒1的 data 覆蓋了

其實還有很多執行緒問題,因為多執行緒是不可控的

4、解決

 service 在的呼叫 excel 實現類的時候,變成區域性變數

在單應用中 合理運用 synchronized 、ReentrantLock、 ThreadLocal 原子應用

分散式 合理運用 redis 鎖

兩種運用,注意死鎖問題

相關文章