简体   繁体   English

从Java线程返回值

[英]Return values from Java Threads

I have a Java Thread like the following: 我有一个类似以下的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)
        }
    }

I have about 300 ids, and every couple of seconds - I fire up threads to make a call for each of the id. 我有大约300个ID,每隔几秒钟 - 我启动线程来为每个id打个电话。 Eg. 例如。

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

Now, I would like to collect the results from each threads, and do a batch insert to the database, instead of making 300 database inserts every 2 seconds. 现在,我想从每个线程收集结果,并对数据库进行批量插入,而不是每2秒进行300次数据库插入。

Any idea how I can accomplish this? 知道我怎么能做到这一点?

The canonical approach is to use a Callable and an ExecutorService . 规范方法是使用CallableExecutorService submit ting a Callable to an ExecutorService returns a (typesafe) Future from which you can get the result. submit一个Callable submitExecutorService返回一个(类型安全的) 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

In your case, you probably want to use invokeAll which returns a List of Futures , or create that list yourself as you add tasks to the executor. 在您的情况下,您可能希望使用invokeAll返回Futures List ,或者在向执行程序添加任务时自己创建该列表。 To collect results, simply call get on each one. 要收集结果,只需在每个结果上调用get

If you want to collect all of the results before doing the database update, you can use the invokeAll method. 如果要在执行数据库更新之前收集所有结果,可以使用invokeAll方法。 This takes care of the bookkeeping that would be required if you submit tasks one at a time, like daveb suggests. 如果您一次提交一项任务,这就照顾了所需的簿记,如daveb建议的那样。

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. */
...

Store your result in your object. 将结果存储在对象中。 When it completes, have it drop itself into a synchronized collection (a synchronized queue comes to mind). 完成后,让它自己放入同步集合(想到一个同步队列)。

When you wish to collect your results to submit, grab everything from the queue and read your results from the objects. 如果您希望收集要提交的结果,请从队列中获取所有内容并从对象中读取结果。 You might even have each object know how to "post" it's own results to the database, this way different classes can be submitted and all handled with the exact same tiny, elegant loop. 你甚至可能让每个对象知道如何将它自己的结果“发布”到数据库中,这样就可以提交不同的类,并且所有类都使用完全相同的微小优雅循环来处理。

There are lots of tools in the JDK to help with this, but it is really easy once you start thinking of your thread as a true object and not just a bunch of crap around a "run" method. JDK中有很多工具可以帮助解决这个问题,但是一旦你开始认为你的线程是一个真正的对象而不仅仅是围绕一个“运行”方法的一堆垃圾,它真的很容易。 Once you start thinking of objects this way programming becomes much simpler and more satisfying. 一旦你开始考虑对象,编程变得更简单,更令人满意。

In Java8 there is better way for doing this using CompletableFuture . 在Java8中,使用CompletableFuture有更好的方法。 Say we have class that get's id from the database, for simplicity we can just return a number as below, 假设我们有从数据库获取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;
    }
}

Now we can add the result to a concurrent collection once the results of every future is ready. 现在,一旦每个未来的结果准备就绪,我们就可以将结果添加到并发集合中。

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);
 }

Now we can add a callback when all the futures are ready, 现在我们可以在所有期货准备就绪时添加回调,

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

You need to store the result in a something like singleton. 你需要将结果存储在像singleton这样的东西中。 This has to be properly synchronized. 这必须正确同步。

EDIT : I know this not the best advice as it is not good idea to handle raw Threads . 编辑 :我知道这不是最好的建议,因为处理原始Threads并不是一个好主意。 But given the question this will work, won't it? 但鉴于这个问题会起作用,不是吗? I may not be upvoted, but why down vote? 我可能不会被投票,但为什么要投票呢?

您可以创建一个队列或列表,将其传递给您创建的线程,线程将其结果添加到列表中,该列表由执行批量插入的使用者清空。

The simplest approach is to pass an object to each thread (one object per thread) that will contain the result later. 最简单的方法是将对象传递给稍后将包含结果的每个线程(每个线程一个对象)。 The main thread should keep a reference to each result object. 主线程应该保持对每个结果对象的引用。 When all threads are joined, you can use the results. 连接所有线程后,您可以使用结果。

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);
            }
        }
}

You could make a class which extends Observable. 你可以创建一个扩展Observable的类。 Then your thread can call a method in the Observable class which would notify any classes that registered in that observer by calling Observable.notifyObservers(Object). 然后,您的线程可以调用Observable类中的方法,该方法将通过调用Observable.notifyObservers(Object)来通知在该观察者中注册的任何类。

The observing class would implement Observer, and register itself with the Observable. 观察类将实现Observer,并向Observable注册自己。 You would then implement an update(Observable, Object) method that gets called when Observerable.notifyObservers(Object) is called. 然后,您将实现一个更新(Observable,Object)方法,该方法在调用Observerable.notifyObservers(Object)时被调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM