简体   繁体   中英

In Java, how to pass the objects back to Main thread from worker threads?

In Java, how to pass the objects back to Main thread from worker threads? Take the following codes as an example:

  main(String[] args) {

    String[] inputs;
    Result[] results;
    Thread[] workers = new WorkerThread[numThreads];

    for (int i = 0; i < numThreads; i++) {
        workers[i] = new WorkerThread(i, inputs[i], results[i]);
        workers[i].start();
    } 

    ....
  }
  ....

class WorkerThread extends Thread {
    String input;
    int name;
    Result result;

    WorkerThread(int name, String input, Result result) {
        super(name+"");
        this.name = name;
        this.input = input;
        this.result = result;
    }

    public void run() {
        result  = Processor.process(input);
    }
}

How to pass the result back to main 's results[i] ?

How about passing this to WorkerThread ,

workers[i] = new WorkerThread(i, inputs[i], results[i], this);

so that it could

mainThread.reults[i] = Processor.process(inputs[i]);

Why don't you use Callable s and an ExecutorService ?

main(String[] args) {

  String[] inputs;
  Future<Result>[] results;

  for (int i = 0; i < inputs.length; i++) {
    results[i] = executor.submit(new Worker(inputs[i]);
  } 
  for (int i = 0; i < inputs.length; i++) {
    Result r = results[i].get();
    // do something with the result
  }
}

One soluton would be to use a callback in the WorkerThread :

class WorkerThread extends Thread {
    ICallback callback;
    ...

    WorkerThread(int name, String input, Result result, ICallback callback) {
        super(name+"");
        this.callback = callback;
    ...
    }

    public void run() {
        result  = Processor.process(input);
        callback.addResult(result);
    }

}

where your calling class would implement addResult and add to the result array.

@Thilo's and @Erickson's answers are the best one. There are existing APIs that do this kind of thing simply and reliably.

But if you want to persist with your current approach of doing it by hand, then the following change to you code may be sufficient:

for (int i = 0; i < numThreads; i++) {
    results[i] = new Result();
    ...
    workers[i] = new WorkerThread(i, inputs[i], results[i]);
    workers[i].start();
}

...

public void run() {
    Result tmp = Processor.process(input);
    this.result.updateFrom(tmp);
    // ... where the updateFrom method copies the state of tmp into
    // the Result object that was passed from the main thread.
}

Another approach is to replace Result[] in the main program with Result[][] and pass a Result[0] to the child thread that can be updated with the result object. (A light-weight holder).

However, there us an Important Gotcha when you are implementing this at a low level is that the main thread needs to call Thread.join on all of the child threads before attempting to retrieve the results. If you don't, there is a risk that the main thread will occasionally see stale values in the Result objects. The join also ensures that the main thread doesn't try to access a Result before the corresponding child thread has completed it.

The main thread will need to wait for the worker threads to complete before getting the results. One way to do this is for the main thread to wait for each worker thread to terminate before attempting to read the result. A thread terminates when its run() method completes.

For example:

for (int i = 0; i < workers.length; i++) {
  worker.join(); // wait for worker thread to terminate
  Result result = results[i]; // get the worker thread's result
  // process the result here...
}

You still have to arrange for the worker thread's result to be inserted into the result[] array somehow. As one possibility, you could do this by passing the array and an index into each worker thread and having the worker thread assign the result before terminating.

Some typical solutions would be:

  • Hold the result in the worker thread's instance (be it Runnable or Thread ). This is similar to the use of the Future interface.
  • Use a BlockingQueue that the worker threads are constructed with which they can place their result into.
  • Simple use the ExecutorService and Callable interfaces to get a Future which can be asked for the result.

It looks like your goal is to perform the computation in parallel, then once all results are available to the main thread, it can continue and use them.

If that's the case, implement your parallel computation as a Callable rather than a thread. Pass this collection of tasks to the invokeAll() method of an ExecutorService . This method will block until all the tasks have been completed, and then your main thread can continue.

I think I have a better solution, why don't you make your worker threads pass the result into a linkedListBlockingQueue, which is passed to them, after they are done, and your main function picks the results up from the queue like this

while(true){linkedListBlockingQueue.take(); 
    //todo: fil in the task you want it to do
    //if a specific kind of object is returned/countdownlatch is finished exit
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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