前言
如果一道题目有5份(输入+输出为一份)测试数据,对于一份用户的代码,如果串行的运行(假设每一份运行平均要500毫秒),那么5份的话就需要2.5秒了。但是,如果我们采用并行的方式的话,则只需要500毫秒(实际上会多一点)就可以了。但是,因为每个线程运行的用户代码,都需要从标准输入流(System.in)中读取数据,而且读取的时机无法掌控,也读需要将运行的输出结果输出到标准输出流(System.out)中,输出的时机也是无法掌控的。因此这里就发生了冲突问题。最明显的情况就是,a,b线程同时向System.out输出数据时,如果我们不做适当处理的话,我们是无法分清哪些输出数据是属于A线程的,哪些是属于B线程的,甚至连输出数据都难以拆分成两份(一个线程输出一份结果数据)。
多线程并行运行
这里主要涉及到两个类:ProblemCallable和ProblemItemCallable。ProblemCallable是一道题目对于一份用户代码的任务类。而ProblemItemCallable是一份数据的任务类。也就是说,一个ProblemCallable会对应多个ProblemItemCallable。他们两者都是实现了Callable接口的类,复写了里面的call方法。
ProblemCallable的call方法代码如下:
public List<ProblemResultItem> call() throws Exception {
List<String> paths = problem.getInputDataFilePathList();
final List<ProblemResultItem> resultItems = new ArrayList<ProblemResultItem>();
countDownLatch = new CountDownLatch(paths.size());
// 为了内存使用比较准确,先大概的执行一次回收吧
run.gc();
for (int i = 0; i < paths.size(); i++) {
final String path = paths.get(i);
itemExecThreadPool.execute(new Runnable() {
@Override
public void run() {
resultItems.add(process(path));