ExecutorService线程池的例子.docx
文本预览下载声明
ExecutorService线程池的例子问题场景考试系统中,当考试结束交卷的同时需要自动批改客观题的答案,由于同时交卷的人数太多,交卷业务会变得很慢,需要等待较长时间,或者服务器容易负载过大而崩溃,因此需要使用线程池和Queue配合来实现这一工作。大体思路如下:每当有学生交卷时,先将学生的答题卡信息(AnswerCard)存入一个全局的Queue。这个操作不涉及数据库读写,会比直接存数据库更快的完成,学生可以很快看到交卷成功,但实际上这时学生交卷并没有完成,只是暂时保存了其答题结果。后台线程池创建5个线程,不断的判断Queue中是否有值,若有值,则一个线程从Queue中取出一个值(AnswerCard对象)进行自动批改,并将批改完成的答题卡存入数据库。程序实现服务启动时立即创建并执行两个线程,一个线程池用于执行自动批改交卷任务,一个清理线程用于定期清理批改过程中产生的垃圾,防止随着时间增加造成内存溢出。批改产生的垃圾是指批改时在内存中存放的Paper(试卷)信息,用于对答题卡内容正确与否进行对比赋分,因为每场考试可能有上百个学生同时交卷,但是正确答案只有一份,因此不需要每次批改时都从数据库中去取一次paper的标准答案进行对比,每次只需取出一个paper对象存入内存,对其他的答题卡进行批改时,直接从内存中获取就可以,基本思路如下:(1)创建一个全局的Mapkey,value,Map的key存放考试的examId,value存放一个自定义的数据结构ExamBean(Paper paper, Date time)。paper是考是关联的试卷,包含标准答案、每题分数等,用于对学生答题卡进行批改赋分的主要对象;time存放考试的结束时间,用于清理线程判断该Map是否已经过期,程序中设置当考试时间过去10小时后即清理对应的Map,防止时间长了内存溢出。(2)当工作线程从Queue中取到一张答题卡进行批改时,会首先根据考试的examId从Map中查找ExamBean,若Map中没有对应的key,则新建一个,并从数据库中查询对应的paper和time组成ExamBean存入Map,此操作对于每个考试只执行一次。(3)清理线程定期检查Map,若Map中存在过期的元素,则remove掉。下面是具体的实现:1)创建一个监听的类GradeListener.java,继承ServletContextListener,如下:package com.geariot.platform.exam.utils;import java.util.Map;import java.util.concurrent.BlockingQueue;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.TimeUnit;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;import net.sf.ehcache.store.chm.ConcurrentHashMap;import org.apache.log4j.Logger;import org.springframework.web.context.WebApplicationContext;import org.springframework.web.context.support.WebApplicationContextUtils;import com.geariot.platform.exam.dao.AnswerCardDao;import com.geariot.platform.exam.entities.AnswerCard;import com.geariot.platform.exam.model.ExamBean;import com.geariot.platform.exam.service.ExamService;import com.geariot.platform.exam.service.PaperService;public class GradeListener implements ServletContextListener {Logger log = Logger.getLogger(GradeListener.class);//创建线程池private ExecutorService service;
显示全部