web-dev-qa-db-ja.com

Javaスレッドからの戻り値

私は次のようなJavaスレッドを持っています:

   public class MyThread extends Thread {
        MyService service;
        String id;
        public MyThread(String id) {
            this.id = node;
        }
        public void run() {
            User user = service.getUser(id)
        }
    }

私は約300のIDを持っており、数秒ごとにスレッドを起動して各IDを呼び出します。例えば。

for(String id: ids) {
    MyThread thread = new MyThread(id);
    thread.start();
}

ここで、各スレッドから結果を収集し、2秒ごとに300回のデータベース挿入を行うのではなく、データベースへのバッチ挿入を実行したいと思います。

どうすればこれを達成できるか考えていますか?

18
Langali

データベースの更新を行う前にすべての結果を収集する場合は、 invokeAll メソッドを使用できます。これにより、 daveb が示唆するように、タスクを一度に1つずつ送信する場合に必要となる簿記が処理されます。

private static final ExecutorService workers = Executors.newCachedThreadPool();

...

Collection<Callable<User>> tasks = new ArrayList<Callable<User>>();
for (final String id : ids) {
  tasks.add(new Callable<User>()
  {

    public User call()
      throws Exception
    {
      return svc.getUser(id);
    }

  });
}
/* invokeAll blocks until all service requests complete, 
 * or a max of 10 seconds. */
List<Future<User>> results = workers.invokeAll(tasks, 10, TimeUnit.SECONDS);
for (Future<User> f : results) {
  User user = f.get();
  /* Add user to batch update. */
  ...
}
/* Commit batch. */
...
18
erickson

正規のアプローチは、CallableExecutorServiceを使用することです。 submitCallableExecutorServiceすると、(typesafe)Futureが返され、そこから結果をgetできます。

class TaskAsCallable implements Callable<Result> {
    @Override
    public Result call() {
        return a new Result() // this is where the work is done.
    }
}

ExecutorService executor = Executors.newFixedThreadPool(300);
Future<Result> task = executor.submit(new TaskAsCallable());
Result result = task.get(); // this blocks until result is ready

あなたの場合、おそらくinvokeAllListを返すFuturesを使用するか、エグゼキュータにタスクを追加するときにそのリストを自分で作成することをお勧めします。結果を収集するには、それぞれでgetを呼び出すだけです。

35
daveb

結果をオブジェクトに保存します。完了したら、同期されたコレクションにドロップします(同期されたキューが思い浮かびます)。

結果を収集して送信する場合は、キューからすべてを取得し、オブジェクトから結果を読み取ります。各オブジェクトに、それ自体の結果をデータベースに「投稿」する方法を知ってもらうこともできます。これにより、さまざまなクラスを送信して、まったく同じ小さなエレガントなループですべて処理できます。

JDKにはこれを支援するツールがたくさんありますが、スレッドを「実行」メソッドの周りの単なるくだらないものではなく、真のオブジェクトとして考え始めると、それは本当に簡単です。このようにオブジェクトについて考え始めると、プログラミングははるかに簡単で満足のいくものになります。

4
Bill K

Java8では、 CompletableFuture を使用してこれを行うためのより良い方法があります。データベースからIDを取得するクラスがあるとします。簡単にするために、次のように数値を返すことができます。

static class GenerateNumber implements Supplier<Integer>{

    private final int number;

    GenerateNumber(int number){
        this.number = number;
    }
    @Override
    public Integer get() {
        try {
            TimeUnit.SECONDS.sleep(1);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        return this.number;
    }
}

これで、すべての将来の結果の準備ができたら、結果を並行コレクションに追加できます。

Collection<Integer> results = new ConcurrentLinkedQueue<>();
int tasks = 10;
CompletableFuture<?>[] allFutures = new CompletableFuture[tasks];
for (int i = 0; i < tasks; i++) {
     int temp = i;
     CompletableFuture<Integer> future = CompletableFuture.supplyAsync(()-> new GenerateNumber(temp).get(), executor);
     allFutures[i] = future.thenAccept(results::add);
 }

これで、すべての先物の準備ができたらコールバックを追加できます。

CompletableFuture.allOf(allFutures).thenAccept(c->{
   System.out.println(results); // do something with result
});
3
Sleiman Jneidi

Observableを拡張するクラスを作成できます。次に、スレッドはObservableクラスのメソッドを呼び出すことができます。このメソッドは、Observable.notifyObservers(Object)を呼び出すことにより、そのオブザーバーに登録されているクラスに通知します。

監視クラスはObserverを実装し、それ自体をObservableに登録します。次に、Observerable.notifyObservers(Object)が呼び出されたときに呼び出されるupdate(Observable、Object)メソッドを実装します。

1
jonescb

結果をシングルトンのようなものに保存する必要があります。これは適切に同期する必要があります。

[〜#〜] edit [〜#〜]:生のThreadsを処理するのは良い考えではないので、これは最善のアドバイスではないことを私は知っています。しかし、これがうまくいくという質問を考えると、そうではありませんか?私は賛成しないかもしれませんが、なぜ反対票を投じるのですか?

1
fastcodejava

作成したスレッドに渡すキューまたはリストを作成できます。スレッドは、バッチ挿入を実行するコンシューマーによって空になるリストに結果を追加します。

1
rsp

最も簡単なアプローチは、後で結果を含むオブジェクトを各スレッドに渡すことです(スレッドごとに1つのオブジェクト)。メインスレッドは、各結果オブジェクトへの参照を保持する必要があります。すべてのスレッドが結合されると、結果を使用できます。

1
AndiDog
public class TopClass {
     List<User> users = new ArrayList<User>();
     void addUser(User user) {
         synchronized(users) {
             users.add(user);
         }
     }
     void store() throws SQLException {
        //storing code goes here
     }
     class MyThread extends Thread {
            MyService service;
            String id;
            public MyThread(String id) {
                this.id = node;
            }
            public void run() {
                User user = service.getUser(id)
                addUser(user);
            }
        }
}
1
Oso