在单独的线程中预创建对象

时间:2009-12-30 18:24:19

标签: java concurrency

假设我有一个类似的Java方法:

public Ouput respond(Input input) { /* .. */ }

Output对象有许多字段 - 其中一些字段依赖于Input对象,但其余字段是预先确定的。我想让调用respond()的线程尽可能快地返回。

为此,我想生成另一个预先创建Output对象的线程,设置一些字段并将它们放入队列中,以便运行respond()的线程可以从队列中获取它,设置剩余的字段并将其返回。

实现这样的事情的最佳方法是什么?我当前的原型使用有界LinkedBlockingQueue,但是有更好的方法来实现这个想法吗?

我的目标是尽快获得respond()方法,因此也欢迎其他符合该目标的建议。 : - )

6 个答案:

答案 0 :(得分:3)

这听起来像是你可以充分利用FutureExecutor的情况。点击此处了解更多信息:

这是一个具体的例子:

ExecutorService executor = Executors.newSingleThreadExecutor();

final Input input = ...
Future<Output> future = executor.submit(new Callable<Output>() {
  public Output call() {
    return respond(input);
  }
});

// do some work

Output output = future.get();  // this blocks until it's done

在您的情况下,由于您提到了Output对象中的某些字段将立即使用,而其他字段将在以后使用,您可以构造您的Output对象以包含期货作为领域。例如:

public class Output {
   private String fieldA; // immediate field
   private Future<String> fieldB; // delayed field

   public void setFieldA(String fieldA) { this.fieldA = fieldA; }
   public String getFieldA() { return fieldA; } 
   public void setFieldB(Future<String> fieldB) { this.fieldB = fieldB; }
   public Future<String> getFieldB() { return fieldB; }
}

然后,您将构建响应方法,如下所示:

public Output respond(final Input input) {
   Output output = new Output();
   String fieldA = ...
   output.setFieldA(fieldA);

   Future<String> fieldBFuture = executor.submit(new Callable<String>() {
     public String call() {
       String fieldB = ...
       return fieldB;
     }
   }

   output.setFieldB(fieldBFuture);
}

并使用您的Output对象:

Input input = ...
Output output = respond(input);

String fieldA = output.getFieldA();
// do some work
String fieldB = output.getFieldB().get();

答案 1 :(得分:2)

也许您可以调整您的实现,即不必为每个响应()调用创建输出对象,而是在不再需要时回收输出对象。重置字段应该比从头创建它们更快,如果您具有高对象吞吐量,它还可以节省您在垃圾收集中的一些时间,还可以提高处理器和Java VM的缓存性能。

当然,这只有在你可以控制整个过程并且可以改变得到response()结果的调用者时才有效。

但如果你真的有这么高的性能要求,这可能是另一个想法。只是不要让它太复杂,不再可维护。

答案 2 :(得分:2)

除非创建输出字段使用的对象相当昂贵,否则使用另一个线程的成本将比设置几个字段的成本高很多倍。

答案 3 :(得分:1)

为什么不创建一个对象创建工厂,在构造时,预先填充Output个对象的内部存储,根据请求将它们分开,并包含逻辑以了解何时需要重新填充内部存储?也许是这样的(请注意,这只是我头脑中的简单代码;有明确的地方你可以做更多的事情,比如有特定的算法来确定池什么时候对象太少,同步人口增长等):

public class OutputFactory {

    private List<Output> outputPool = new LinkedList<Output>();

    public OutputFactory() {
        this.populatePool();
    }

    private void populatePool() {
        // spawn a new thread here, if you wish, to optimize time-to-return
        for (int i = 0; i < 100; i++) {
            outputPool.add(new Output());
        }
    }

    public Output getNewOutput() {
        Output newOutput = outputPool.remove(0);
        if (outputPool.size() < 10) {
            populatePool();
        }
        return newOutput;
    }

}

答案 4 :(得分:1)

由于所有线程共享同一个堆,我无法想象在分配方面,在不同的线程上创建它们会产生很大的不同。如果您看到线程的好处,那将是由于在Output对象上设置预定字段所需的任何计算。

如果是这种情况,您可能会更好地拥有预先填充预定字段的单个模板Output对象,然后只需respond()克隆共享模板对象。 (如果对象不是Cloneable而你不能这样做,那么自己复制字段的成本大致相同。)

如果模板的东西不起作用,但你可以回收输出实例,这可能是一个更好的选择,因为根本不需要分配新的内存,甚至不需要复制预定的字段;他们已经确定了。

这些策略中的任何一个都可以让您加速respond()方法,而不会产生单独线程的开销。

答案 5 :(得分:0)

如果其余字段真正“预先确定”,为什么不在Output类上使它们静态?