1

私は、単一のリクエストで何百ものデータをロードする必要があるWebアプリケーションを持っています。問題は、データが分散していることです。そのため、複数の場所からデータをロードし、それらにフィルターを適用し、処理してから応答する必要があります。これらすべての操作を順番に実行すると、サーブレットが遅くなります。

そのため、すべてのデータを のような個別のスレッドにロードし、t[i] = new Thread(loadData).start();すべてのスレッドの使用が終了するのを待ち、完了したらデータwhile(i < count) t[i].join();を結合して応答することを考えました。

このアプローチが正しいのか、それとももっと良い方法があるのか​​ わかりません。どこかで読んだことがありますが、サーブレットでスレッドを生成することはお勧めできません。

私の目的のコードは次のようになります。

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!
}
4

4 に答える 4

6

主な問題は、生成できるスレッドの数を制限しないため、サーブレットに対して多数の同時リクエストが発生した場合にサーバーを停止させることです。もう1つの問題は、新しいスレッドを再利用するのではなく作成し続けることです。これは非効率的です。

これらの2つの問題は、スレッドプールを使用することで簡単に解決できます。そして、Javaはそれらをネイティブにサポートしています。チュートリアルを読んでください。

また、ServletContextListenerを使用して、Webアプリケーションがシャットダウンされたときにスレッドプールをシャットダウンするようにしてください。

于 2013-02-09T10:54:00.853 に答える
0

すべてのスレッドが完了するのを待ってから応答を提供しているため、CPU サイクルのみを使用している場合、IMO 複数のスレッドは役に立ちません。スレッドにコンテキスト切り替えの遅延を追加することで、応答時間が増加するだけです。シングルスレッドの方が良いでしょう。ただし、ネットワーク/IO などが関係している場合は、スレッド プールを利用できます。

しかし、あなたはあなたのアプローチを再検討したいと思います。http リクエストで大量のデータを同期的に処理することはお勧めできません。エンド ユーザーにとって良い経験にはなりません。できることは、スレッドを開始してデータを処理し、「処理中です」という応答を提供することです。Web ユーザーがいつでもステータスを確認できるように、なんらかのジェスチャを提供できます。

于 2013-03-08T07:34:15.580 に答える
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());
}
  }
}
于 2013-02-09T11:01:52.127 に答える
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);既存のスレッド プールを再利用することをお勧めします。

于 2013-02-09T11:18:28.327 に答える