模板方法中的线程安全问题
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 锁
两种运用,注意死锁问题
蓝天和白云是标配。