12

次の問題が発生します。パフォーマンス上の理由から、作業を複数のスレッドに分割する必要がありますが、どのようなアプローチを取るべきかわかりません。

まず、私が提供するタスクは、値を返し、パラメーターを受け取る必要があります。さらに、mainメソッド(ではなく、メインビットの作業をstatic main()実行する)はすでに別のスレッドで実行されており、定期的に呼び出されます。また、このメソッドは、すべてのスレッドが終了して続行するまで、ある時点で待機する必要があります。

1つのアプローチ(私にとって最も明白な)は、各ジョブを別々のスレッドでスケジュールし、結果をクラス変数に格納することです。

public Object result1, result2;

public void mainMethod() throws InterruptedException {
    final Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            result1 = expensiveMethod("param1");
        }
    });

    final Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            result2 = expensiveMethod("param2");
        }
    });

    thread1.join();
    thread.join();

    //Do rest of work
}

private Object expensiveMethod(Object param){
    // Do work and return result
}

私が言ったように、mainMethodは何度も呼び出され、結果変数の設定に競合状態が必要ないため、これは少し醜く、理想的ではありません。理想的には、それらをローカル変数にしたいのですが、それらがfinalでない限り、runメソッド内からアクセス可能にすることはできず、値を割り当てることはできません...

私がやることについて私が考えたもう一つのアプローチはこれでした:

public void mainMethod() throws InterruptedException, ExecutionException {
    String obj1, obj2;

    final ExecutorService executorService = Executors.newFixedThreadPool(16);
    final Future<String> res1 = executorService.submit(new Callable<String>() {
        @Override
        public String call() throws Exception {
            return expensiveMethod("param1");
        }
    });
    final Future<String> res2 = executorService.submit(new Callable<String>() {
        @Override
        public String call() throws Exception {
            return expensiveMethod("param2");
        }
    });

    obj1 = res1.get();
    obj2 = res2.get();

}

private String expensiveMethod(String param) {
    // Do work and return result
}

これにより、mainメソッドからのこれら2つの計算が自動的に待機され、結果をローカルに保存できます。皆さんはどう思いますか?他のアプローチはありますか?

4

6 に答える 6

15

を使用したアプローチExecutorServiceは、これを行うための最も現代的で安全な方法です。Callableを別のクラスに抽出することをお勧めします。

public class ExpensiveTask implements Callable<String> {

    private final String param;

    public ExpensiveTask(String param) {
        this.param = param;
    }

    @Override
    public String call() throws Exception {
        return expensiveMethod(param);
    }

}

これにより、コードがよりクリーンになります。

final ExecutorService executorService = Executors.newFixedThreadPool(16);
final Future<String> res1 = executorService.submit(new ExpensiveTask("param1"));
final Future<String> res2 = executorService.submit(new ExpensiveTask("param2"));
String obj1 = res1.get();
String obj2 = res2.get();

いくつかの注意:

  • 2つのタスクを同時に処理するだけの場合、または複数のクライアントスレッドからそのプールを再利用する場合は、16スレッドでは多すぎますか?

  • プールを閉じることを忘れないでください

  • ライトExecutorCompletionServiceウェイトを使用して、最初に送信されたタスクではなく、最初のタスクが終了するのを待ちます。

まったく異なるデザインのアイデアが必要な場合は、アクターベースの並行性モデルを備えた

于 2012-10-11T18:13:17.583 に答える
2

まず、からの作成を外部化することをお勧めしExecutorServiceます。mainMethod()これが頻繁に呼び出される場合は、多くのスレッドを作成している可能性があります。

Futureこれがまさに先物の目的であるため、アプローチの方が優れています。また、コードの読み取りが非常に簡単になります。

簡単に言うと、オブジェクトをfinalとして定義する必要があるかもしれませんが、参照がfinalであるかどうかに関係なく、オブジェクトにsetterメソッドをいつでも呼び出すことができ、finalオブジェクトの値を変更できる可能性があります。(参照は最終的なオブジェクトではありません!)

于 2012-10-11T18:17:57.833 に答える
2

少し異なるアプローチは次のとおりです。

  • LinkedBlockingQueueを作成します

  • それを各タスクに渡します。タスクは、スレッド、またはjucExecutorの実行可能ファイルにすることができます。

  • 各タスクはその結果をキューに追加します

  • メインスレッドは、ループ内でqueue.take()を使用して結果を読み取ります

このようにして、結果は計算されるとすぐに処理されます。

于 2012-10-11T18:39:57.473 に答える
2

CompletionServiceを使用して、送信されたタスクを追跡する必要があります。
次に、ループでtake()を実行し、すべてのタスクが完了したらループを終了します。
後でタスクを追加することで、非常にうまくスケーリングできます。

于 2012-10-12T17:28:38.007 に答える
1

パラメータ化されたもののためにまったく新しいクラスを作成するよりも、私の目にはよりエレガントな提案を追加しますCallableCallable私の解決策は、インスタンスを返すメソッドです。

Callable<String> expensive(final String param) {
  return new Callable<String>() { public String call() { 
    return expensiveMethod(param);
  }};
}

これにより、クライアントコードがさらに美味しくなります。

final Future<String> f1 = executor.submit(expensive("param1"));
于 2012-10-11T18:35:36.537 に答える
1
private static final ExecutorService threadpool = Executors.newFixedThreadPool(3);

    ArrayList<Future<List<Data>>> futures = new ArrayList<Future<List<Data>>>();
    for (ArrayList<Data> data : map.values()) {
        final Future<List<Data>> future = threadpool.submit(new ValidationTaskExecutor(data));
        futures.add(future);
    }
    List<List<Data>> res = new ArrayList<List<Data>>();
    for (Future<List<Data>> future : futures) {
        allValidRequest.addAll(future.get());

    }
于 2016-02-01T05:00:34.113 に答える