我不久前参与了一个项目,该项目的报告流程如下:
- 用户会要求举报
- 报告要求将被翻译成较小的部分
- 每个零件的报告将基于零件/节的类型由报告生成器生成
- 组成报告的各个部分将重新组合成最终报告,并返回给用户
我的目标是展示如何从错误的实施过渡到相当好的实施:
单元测试最好地展示了我拥有的一些基本构建基块:这是一个测试助手,它生成示例报告请求,其中包括组成报告请求部分:
public class FixtureGenerator {public static ReportRequest generateReportRequest(){List<ReportRequestPart> requestParts = new ArrayList<ReportRequestPart>();Map<String, String> attributes = new HashMap<String, String>();attributes.put("user","user");Context context = new Context(attributes );ReportRequestPart part1 = new ReportRequestPart(Section.HEADER, context);ReportRequestPart part2 = new ReportRequestPart(Section.SECTION1, context);ReportRequestPart part3 = new ReportRequestPart(Section.SECTION2, context);ReportRequestPart part4 = new ReportRequestPart(Section.SECTION3, context);ReportRequestPart part5 = new ReportRequestPart(Section.FOOTER, context); requestParts.add(part1); requestParts.add(part2);requestParts.add(part3);requestParts.add(part4);requestParts.add(part5);ReportRequest reportRequest = new ReportRequest(requestParts );return reportRequest;}}
以及生成报告的测试:
public class FixtureGenerator {@Testpublic void testSequentialReportGeneratorTime(){long startTime = System.currentTimeMillis();Report report = this.reportGenerator.generateReport(FixtureGenerator.generateReportRequest());long timeForReport = System.currentTimeMillis()-startTime;assertThat(report.getSectionReports().size(), is (5));logger.error(String.format("Sequential Report Generator : %s ms", timeForReport));}
生成报告一部分的组件是一个虚拟实现,具有2秒的延迟以模拟IO密集调用:
public class DummyReportPartGenerator implements ReportPartGenerator{@Overridepublic ReportPart generateReportPart(ReportRequestPart reportRequestPart) {try {//Deliberately introduce a delayThread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}return new ReportPart(reportRequestPart.getSection(), "Report for " + reportRequestPart.getSection());}
}
顺序执行
给定这些基本的类集,我的第一个天真的顺序实现如下:
public class SequentialReportGenerator implements ReportGenerator {private ReportPartGenerator reportPartGenerator;@Overridepublic Report generateReport(ReportRequest reportRequest){List<ReportRequestPart> reportRequestParts = reportRequest.getRequestParts();List<ReportPart> reportSections = new ArrayList<ReportPart>();for (ReportRequestPart reportRequestPart: reportRequestParts){reportSections.add(reportPartGenerator.generateReportPart(reportRequestPart));}return new Report(reportSections);}......
}
显然,对于其中包含5个部分的报告请求,每个部分需要2秒钟才能完成,此报告大约需要10秒钟才能返回给用户。
它请求同时进行。
基于原始线程的实现
以下是第一个并发实现,虽然不好,但比顺序的要好,其后是为每个报告请求部分生成一个线程,等待要生成的报告部分(使用thread.join()方法),并在出现这些块时对其进行汇总在。
public class RawThreadBasedReportGenerator implements ReportGenerator {private static final Logger logger = LoggerFactory.getLogger(RawThreadBasedReportGenerator.class);private ReportPartGenerator reportPartGenerator;@Overridepublic Report generateReport(ReportRequest reportRequest) {List<ReportRequestPart> reportRequestParts = reportRequest.getRequestParts();List<Thread> threads = new ArrayList<Thread>();List<ReportPartRequestRunnable> runnablesList = new ArrayList<ReportPartRequestRunnable>();for (ReportRequestPart reportRequestPart : reportRequestParts) {ReportPartRequestRunnable reportPartRequestRunnable = new ReportPartRequestRunnable(reportRequestPart, reportPartGenerator);runnablesList.add(reportPartRequestRunnable);Thread thread = new Thread(reportPartRequestRunnable);threads.add(thread);thread.start();}for (Thread thread : threads) {try {thread.join();} catch (InterruptedException e) {logger.error(e.getMessage(), e);}}List<ReportPart> reportParts = new ArrayList<ReportPart>();for (ReportPartRequestRunnable reportPartRequestRunnable : runnablesList) {reportParts.add(reportPartRequestRunnable.getReportPart());}return new Report(reportParts);} .....
}
这种方法的危险在于,将为每个报表部件创建一个新线程,因此在实际情况下,如果同时发出100个请求,并且每个请求都产生5个线程,则可能最终在vm中创建500个代价高昂的线程!!
因此,必须以某种方式限制线程的创建。 在下一篇博客文章中,我将介绍另外两种控制线程的方法。
参考: 并发–来自JCG合作伙伴 Biju Kunjummen的all和杂物博客, 并发-顺序和原始线程 。
翻译自: https://www.javacodegeeks.com/2012/07/concurrency-sequential-and-raw-thread.html