1.使用CompletionService来进行级联
先完成上一步的任务先进入下一阶段的处理,提高处理效率(吞吐量)!
2.使用的缓存的本质
缓存热点数据。
在软件中使用缓存的目的和计算机系统使用缓存的目的本质是一致的,所以其更新算法可以参考《现代操作系统》第3章——3.4页面置换算法。
3.多线程的安全问题
如下可能发生两个线程处理同一个题目,这种情况如果心中没有多线程的概念,会很容易出错。一般采用代码中的方法或者CAS来解决。
private static Future<QuestionInCacheVo> getQstFuture(Integer questionId){
Future<QuestionInCacheVo> questionFuture
= processingQuestionCache.get(questionId);
try {
if(questionFuture==null) {
QuestionInDBVo qstDbVo = SL_QuestionBank.getQuetion(questionId);
QuestionTask questionTask = new QuestionTask(qstDbVo,questionId);
/*不靠谱的,无法避免两个线程处理同一个题目
questionFuture = makeQuestionService.submit(questionTask);
processingQuestionCache.putIfAbsent(questionId, questionFuture);
改成
processingQuestionCache.putIfAbsent(questionId, questionFuture);
questionFuture = makeQuestionService.submit(questionTask);
也不行
*/
FutureTask<QuestionInCacheVo> ft
= new FutureTask<QuestionInCacheVo>(questionTask);
questionFuture = processingQuestionCache.putIfAbsent(questionId,
ft);
if(questionFuture==null) {
//先在map中占位
questionFuture = ft;
makeQuestionService.execute(ft);
System.out.println("成功启动了题目["+questionId+"]的计算任务,请等待完成>>>>>>>>");
}else {
System.out.println("<<<<<<<<<<<有其他线程刚刚启动了题目["+questionId
+"]的计算任务,本任务无需开启!");
}
}else {
System.out.println("题目[]已存在计算任务,无需重新生成.");
}
} catch (Exception e) {
processingQuestionCache.remove(questionId);
e.printStackTrace();
throw e;
}
return questionFuture;
}
4.摘要——比较题目是否发生更新
在计算机软件系统中,要尽可能减少耗时操作的比例、或者说优化耗时操作、或者说避免不必要的操作。
在这个层次上相通的途径:内存池与连接池。用更少的唯一标识来代表一个大的数据:有数据库中的键、数据结构中的唯一标识ID(如UUID)
5.其他
1、性能优化一定要建立在对业务的深入分析上,比如我们在性能优化的切入点,在缓存数据结构的选择就建立在对业务的深入理解上;
2、性能优化要善于利用语言的高并发特性,性能优化多多利用缓存,异步任务等机制,正是因为我们使用这些特性和机制,才让我们的应用在性能上有个了质的飞跃;
3、引入各种机制的同时要注意避免带来新的不安全因素和瓶颈,比如说缓存数据过期的问题,并发时的线程安全问题,都是需要我们去克服和解决的。
参考
- 1)享学课堂Mark老师笔记
- 2)缓存更新算法请参考《现代操作系统》第3章——3.4页面置换算法