我已经创建了一个包含4个工作线程的池来处理一些文件。在测试中,大约有200个。线程池版本已经比顺序执行快3倍,但还有改进的余地。
最大的瓶颈(忽略磁盘I / O)是我需要实例化一个新的MessageDigest对象。在单线程版本中我只有1.在这个版本中我有200个。
我想知道的是,工作池中的线程是否可以有一个本地变量?这样(假设没有线程死亡),MessageDigest对象只有四个实例,而不是200 ......
每个任务都需要摘要,所以我不确定是否有更好的方法来做...
我尝试使用ThreadLocal对象但是应该在哪里创建它?如果我在任务本身创建它,我猜它在任务完成时就会脱离上下文。每次创建新实例时。我的代码是:
ThreadLocal<GenerateSHA1> tl = new ThreadLocal<GenerateSHA1>();
hashMaker = tl.get();
if(hashMaker == null){
hashMaker = new GenerateSHA1();
tl.set(hashMaker);
}
这是从任务的构造函数内部完成的。
更新
好吧,让它成为静态的工作,因为对象不会丢失 - 但它现在突出显示了一个不同的问题。工作“任务”在主线程中创建,然后使用invokeAll()添加到ExecutorService。
有关如何解决此问题的任何想法?
答案 0 :(得分:3)
为您的课程延长ThreadLocal
并覆盖initialValue()
方法。默认情况下,它返回null。
private static class ThreadLocalGenerateSHA1 extends
ThreadLocal<GenerateSHA1> {
@Override
protected GenerateSHA1 initialValue() {
return new GenerateSHA1();
}
}
private static final ThreadLocalGenerateSHA1 generateSHA1 = new ThreadLocalGenerateSHA1();
...
在任务上,只需调用generateSHA1的get()
方法即可。您无需致电set()
。
答案 1 :(得分:0)
您可以通过扩展它来获取ThreadPoolExecutor的beforeExecute和afterExecute方法的帮助。在扩展类中,创建4个MessageDigest对象,并以循环方式将它们分配给beforeExecute(...)中的每个任务。
private static final Executor executor = new MyThreadPoolExecutor(10, 10, 50000L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(100));
我觉得Ravi Bhatt提到的对象池选项是一个简单而优雅的想法。创建一个池,让任务从池中请求MessageDigest。
答案 2 :(得分:0)
您可以将对象池用于邮件摘要对象。在您的情况下,将池大小设置为4。
apache commons提供了出色的pool api: http://commons.apache.org/pool/