假设我有一个类似的Java方法:
public Ouput respond(Input input) { /* .. */ }
Output
对象有许多字段 - 其中一些字段依赖于Input
对象,但其余字段是预先确定的。我想让调用respond()
的线程尽可能快地返回。
为此,我想生成另一个预先创建Output
对象的线程,设置一些字段并将它们放入队列中,以便运行respond()
的线程可以从队列中获取它,设置剩余的字段并将其返回。
实现这样的事情的最佳方法是什么?我当前的原型使用有界LinkedBlockingQueue
,但是有更好的方法来实现这个想法吗?
我的目标是尽快获得respond()
方法,因此也欢迎其他符合该目标的建议。 : - )
答案 0 :(得分:3)
这听起来像是你可以充分利用Future
和Executor
的情况。点击此处了解更多信息:
这是一个具体的例子:
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类上使它们静态?