有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

ExecutorService(Java)中的并发持久变量

我已经创建了一个由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中

关于如何解决这个问题有什么想法吗


共 (3) 个答案

  1. # 1 楼答案

    您可以将对象池用于消息摘要对象。在您的案例中,将池大小设置为4

    apache commons提供了出色的池api: http://commons.apache.org/pool/

  2. # 2 楼答案

    为类扩展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()

  3. # 3 楼答案

    您可以通过扩展ThreadPoolExecutor的beforeExecuteafterExecute方法来获得帮助。在扩展类中,创建4个MessageDigest对象,并将它们分配给beforeExecute(…)中的每个任务以循环的方式

    private static final Executor executor = new MyThreadPoolExecutor(10, 10, 50000L,   TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(100));
    

    编辑

    我觉得Ravi Bhatt提到的对象池选项是一个简单而优雅的想法。创建一个池,让任务从池中请求MessageDigest