模板方法中的线程安全问题

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 锁

两种运用,注意死锁问题

posted @ 2023-05-05 11:45  linzm14  阅读(191)  评论(0编辑  收藏  举报