J2EE 环境 - 多线程 - 从不同的服务中并行获取数据
J2EE environment- Multithreading - Fetching data from different services in parallel
对于我在下面使用的内容,是否有更好的替代方法(design/performance/memory 优化):
问题陈述:在 J2EE 环境中,当请求到来时,我的控制器需要从三个不同的服务获取数据(例如,第一个获取天气数据,第二个获取交通密度,最后一个获取当前没有外星人访问我们).如果一项服务需要 5 秒(最好的情况)来获取,那么以同步方式它们至少需要 15 秒。但控制器不应超过 6-7 秒。所以最终调用不同的线程。但可能存在服务花费超过 10 秒的情况(可能是最坏的情况)。
So I thought of creating a thread wait, notify using an observable pattern with a small state maintaining. It was working, but not happy
with the design.
Then I found Java 'ForkJoinPool'. For each request controller creates a new 'ForkJoinPool
' instance - now not creating it on each request. Created a custom
class (say ForkService
) extending RecursiveAction
which has a
List<RecursiveAction>
. And ForkService's compute method has
@Override
protected void compute() {
for (RecursiveAction recursiveAction : actions) {
recursiveAction.fork(); // async calling of all other service's compute method which actually fetches the data from the relevant service.
}
mPool.shutdown();
}
目前正在使用,效果不错。想知道有没有更好的方法来做到这一点?
一个简单优雅的方法是使用固定的线程池和Guava的ListenableFuture
on which you can call Futures.successfulAsList
:
private MyResult getResult(MyRequest request) {
ExecutorService es = Executors.newFixedThreadPool(3);
ListeningExecutorService les = MoreExecutorslisteningDecorator(es);
ListenableFuture<?> lf1 = les.submit(getCallableForService1(request));
ListenableFuture<?> lf2 = les.submit(getCallableForService2(request));
ListenableFuture<?> lf3 = les.submit(getCallableForService3(request));
ListenableFuture<List<?>> lfs = Futures.successfulAsList(lf1, lf2, lf3);
// wait 7 sec for results
List<?> res = lfs.get(7, TimeUnit.SEONDS);
return extractRes(res);
}
您当然应该为 Callable
处理正确的类型。
我对 Fork/Join framework 不太熟悉,但我可以马上告诉您,为每个请求创建一个新的线程池会破坏线程池的一半用途。线程的创建和销毁成本很高,使用线程池以便您可以重用线程。
查看文档,似乎 Fork/Join 旨在解决本质上递归且需要密集处理的问题。您的问题似乎不符合这两个条件。
您最好使用常规 ThreadPoolExecutor that's shared by all of your requests. The invokeAll 方法可能会特别有用;您可以一次提交所有 3 个请求,收到 List<Future<T>>
,如果您愿意,甚至可以提供超时。
要获得结果,您可以简单地遍历 List<Future<Thing>> things
:
Thing[] results = new Thing[3];
for (int i = 0; i < things.size(); ++i) {
results[i] = things.get(i).get();
}
好久没写了,想着写点别的可以的:
1.回调的使用。当线程完成工作时,它会通过回调进行响应。
2.通知:每个线程发送工作完成通知。
3.问题中提到的RecursiveAction
的使用
4. 使用 Guava 库的 ListenableFuture
的答案被接受为最终答案。
对于我在下面使用的内容,是否有更好的替代方法(design/performance/memory 优化):
问题陈述:在 J2EE 环境中,当请求到来时,我的控制器需要从三个不同的服务获取数据(例如,第一个获取天气数据,第二个获取交通密度,最后一个获取当前没有外星人访问我们).如果一项服务需要 5 秒(最好的情况)来获取,那么以同步方式它们至少需要 15 秒。但控制器不应超过 6-7 秒。所以最终调用不同的线程。但可能存在服务花费超过 10 秒的情况(可能是最坏的情况)。
So I thought of creating a thread wait, notify using an observable pattern with a small state maintaining. It was working, but not happy with the design.
Then I found Java 'ForkJoinPool'. For each request controller creates a new '
ForkJoinPool
' instance - now not creating it on each request. Created a custom class (sayForkService
) extendingRecursiveAction
which has aList<RecursiveAction>
. And ForkService's compute method has
@Override
protected void compute() {
for (RecursiveAction recursiveAction : actions) {
recursiveAction.fork(); // async calling of all other service's compute method which actually fetches the data from the relevant service.
}
mPool.shutdown();
}
目前正在使用,效果不错。想知道有没有更好的方法来做到这一点?
一个简单优雅的方法是使用固定的线程池和Guava的ListenableFuture
on which you can call Futures.successfulAsList
:
private MyResult getResult(MyRequest request) {
ExecutorService es = Executors.newFixedThreadPool(3);
ListeningExecutorService les = MoreExecutorslisteningDecorator(es);
ListenableFuture<?> lf1 = les.submit(getCallableForService1(request));
ListenableFuture<?> lf2 = les.submit(getCallableForService2(request));
ListenableFuture<?> lf3 = les.submit(getCallableForService3(request));
ListenableFuture<List<?>> lfs = Futures.successfulAsList(lf1, lf2, lf3);
// wait 7 sec for results
List<?> res = lfs.get(7, TimeUnit.SEONDS);
return extractRes(res);
}
您当然应该为 Callable
处理正确的类型。
我对 Fork/Join framework 不太熟悉,但我可以马上告诉您,为每个请求创建一个新的线程池会破坏线程池的一半用途。线程的创建和销毁成本很高,使用线程池以便您可以重用线程。
查看文档,似乎 Fork/Join 旨在解决本质上递归且需要密集处理的问题。您的问题似乎不符合这两个条件。
您最好使用常规 ThreadPoolExecutor that's shared by all of your requests. The invokeAll 方法可能会特别有用;您可以一次提交所有 3 个请求,收到 List<Future<T>>
,如果您愿意,甚至可以提供超时。
要获得结果,您可以简单地遍历 List<Future<Thing>> things
:
Thing[] results = new Thing[3];
for (int i = 0; i < things.size(); ++i) {
results[i] = things.get(i).get();
}
好久没写了,想着写点别的可以的:
1.回调的使用。当线程完成工作时,它会通过回调进行响应。
2.通知:每个线程发送工作完成通知。
3.问题中提到的RecursiveAction
的使用
4. 使用 Guava 库的 ListenableFuture
的答案被接受为最终答案。