我的多线程代码需要帮助。我有一个可调用的类,它返回一个值。我有一个cachedThreadPool,可以提交大约60000个任务。我收集了一个列表中的所有期货。ExecutiveService关闭后,我循环浏览Futures列表,并使用bufferedWriter写入返回值。这是正确的实施方式吗?
ExecutorService execService = Executors.newCachedThreadPool();
List<Future<ValidationDataObject<String, Boolean>>> futureList = new ArrayList<>();
for (int i = 0; i < emailArrayList.size(); i++) {
String emailAddress = emailArrayList.get(i);
ValidateEmail validateEmail = new ValidateEmail(emailAddress);
Future<ValidationDataObject<String, Boolean>> future =
execService.submit(validateEmail);
futureList.add(future);
}
execService.shutdown();
for (Future<ValidationDataObject<String, Boolean>> future: futureList) {
ValidationDataObject<String, Boolean> validationObject = future.get();
bufferedWriter.write(validationObject.getEmailAddress() + "|"
+ validationObject.getIsValid());
bufferedWriter.newLine();
bufferedWriter.flush();
}
if (execService.isTerminated()) bufferedWriter.close();
我应该为bufferedWriter使用同步块吗?我在想,它不需要同步,因为我正在使用主线程中的bufferedWriter,对吧?
我有一个cachedThreadPool来提交大约60000个任务。
缓存的线程池和60k任务是一个危险信号。这将启动60k个线程,我怀疑你是否真的想要。您应该使用一个固定的线程池,并改变线程数量,直到您在吞吐量和服务器负载之间取得良好的平衡。也许从2倍的CPU数量开始,然后根据服务器负载的不同而变化。
您也可以考虑使用固定大小的队列,这将限制未完成任务的数量,尽管60k任务是可以的,除非这些对象很重。
我收集列表中的所有期货。ExecutiveService关闭后,我循环浏览Futures列表,并使用bufferedWriter写入返回值。这是正确的实施方式吗?
是的,这是一个很好的模式。你没有展示作者是被创造的,但主线程拥有它当然是好的。
我应该为bufferedWriter使用同步块吗?我在想,它不需要同步,因为我正在使用主线程中的bufferedWriter,对吧?
右。没有其他线程在使用它,所以这很好。让编写器线程管理多线程应用程序的输出是一种非常典型的模式。
最后一点是,您可能希望查看ExecutionCompletionService
,它允许您在任务完成时处理任务,而不必按顺序等待任务。您可能要求按顺序输出,在这种情况下,这没有帮助,但无论如何,这是一项很好的技术。
除了executor.shutdown()
很可能不会做你认为它会做的事情(它只是阻止执行器接受新任务,它不会等待所有任务终止)之外,你的代码看起来很好。
您是对的,不需要对编写器进行同步,因为您只能单线程访问它。
不过,有些事情是可以改进的。首先,您没有进行大量的异常处理。如果Callable
命中Exception
,则Future.get()
将抛出ExecutionException
。
我不确定Callables
的执行时间偏差有多大。假设在以下情况下存在显著偏差:假设我们提交Callable
的A、B和C,您收到的是FutA、FutB和FutC。调用get
方法将被阻塞,直到Future
后面的计算完成。在您的设置中,您可能正在等待FutA完成,而FutB/FutC可能已经完成并准备好写入。最糟糕的情况是,对A的处理将延迟所有60000个任务的写入。
我想,我会采用另一种方法,即每个Callable
都获得对同一ConcurrentLinkedQueue
的引用,而不是通过Future
返回结果,将结果写入该队列。在这种情况下,结果的顺序不取决于Callable
s的顺序,而是取决于Callable
s完成执行的时间。这是否会导致加速取决于您的设置(尤其是写入结果的时间和Callable
的执行时间偏差)。