我有一个Web应用程序,在单个请求中可能需要加载数百个数据。现在的问题是数据分散了。所以,我必须从几个地方加载数据,对它们应用过滤器,处理它们然后响应。顺序执行所有这些操作会使servlet 慢 !
所以我想到将所有数据加载到单独的线程中,如t[i] = new Thread(loadData).start();
,等待所有线程使用while(i < count) t[i].join();
完成,完成后,加入数据并做出响应。
现在我不确定这种方法是否正确或是否有更好的方法。我在某处读过,不建议在servlet中生成线程。
我想要的代码看起来像这样。
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
Iterable<?> requireddata = requiredData(request);
Thread[] t = new Thread[requireddata.size];
int i = 0;
while (requireddata.hasNext())
{
t[i] = new Thread(new loadData(requiredata.next())).start();
i++;
}
for(i = 0 ; i < t.length ; i++)
t[i].join();
// after getting the data process and respond!
}
答案 0 :(得分:6)
主要问题是,如果许多并发请求进入servlet,您将使服务器瘫痪,因为您没有限制可以生成的线程数。另一个问题是你不断创建新线程而不是重用它们,这是低效的。
使用线程池可以轻松解决这两个问题。 Java对它们有本机支持。阅读the tutorial。
此外,确保在关闭webapp时使用ServletContextListener关闭线程池。
答案 1 :(得分:0)
您可以考虑使用java.util.concurrent api中的Executor框架。例如,您可以将计算任务创建为Callable,然后将该任务提交给ThreadPoolExecutor。来自Java Concurrency in Practice的示例代码: -
public class Renderer {
private final ExecutorService executor;
Renderer(ExecutorService executor) { this.executor = executor; }
void renderPage(CharSequence source) {
final List<ImageInfo> info = scanForImageInfo(source);
CompletionService<ImageData> completionService =
new ExecutorCompletionService<ImageData>(executor);
for (final ImageInfo imageInfo : info)
completionService.submit(new Callable<ImageData>() {
public ImageData call() {
return imageInfo.downloadImage();
}
});
renderText(source);
try {
for (int t = 0, n = info.size(); t < n; t++) {
Future<ImageData> f = completionService.take();
ImageData imageData = f.get();
renderImage(imageData);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
throw launderThrowable(e.getCause());
}
}
}
答案 2 :(得分:0)
听起来像是CyclicBarrier的问题。
例如:
ExecutorService executor = Executors.newFixedThreadPool(requireddata.size);
public void executeAllAndAwaitCompletion(List<? extends T> threads){
final CyclicBarrier barrier = new CyclicBarrier(threads.size() + 1);
for(final T thread : threads){
executor.submit(new Runnable(){
public void run(){
//it is not a mistake to call run() here
thread.run();
barrier.await();
}
});
}
barrier.await();
}
一旦所有其他人完成,threads
的最后一个帖子就会被取消。
不是调用Executors.newFixedThreadPool(requireddata.size);
,而是重用一些现有的线程池。
答案 3 :(得分:0)
由于您正在等待所有线程完成,然后您正在提供响应,如果您只使用CPU周期,IMO多线程将无法帮助。它只会通过在线程中添加上下文切换延迟来增加响应时间。单个线程会更好。但是,如果涉及网络/ IO等,您可以使用线程池。
但是你想重新考虑你的方法。不建议在http请求中同步处理大量数据。对最终用户来说不是一个好的体验。你可以做的是启动一个线程来处理数据并提供一个回复说“正在处理”。您可以为Web用户提供某种姿势,以便随时检查状态。