Ожидание значения потоков Java - PullRequest
1 голос
/ 18 октября 2011

У меня следующая ситуация:

Чтобы запустить алгоритм, я должен запустить несколько потоков, и каждый поток установит переменную экземпляра x прямо перед его смертью. Проблема в том, что эти потоки не возвращаются немедленно:

public Foo myAlgorithm()
{
    //create n Runnables (n is big)
    //start these runnables (may take long time do die)

    //i need the x value of each runnable here, but they havent finished yet!

    //get average x from all the runnables

    return new Foo(averageX);
}

Должен ли я использовать ожидание уведомления? Или я должен просто вставить цикл while и проверить его на завершение?

Спасибо всем!

Ответы [ 3 ]

4 голосов
/ 18 октября 2011

Создайте общее хранилище для хранения значения x из каждого потока или просто сохраните сумму, если этого достаточно.Используйте CountDownLatch, чтобы дождаться завершения потоков.Каждый поток по завершении будет вызывать CountDownLatch.countDown(), а ваш метод myAlgorithm будет использовать метод CountDownLatch.await() для их ожидания.

Изменить: Вот полный пример подхода, который я предложил.Он создал 39 рабочих потоков, каждый из которых добавляет случайное число к общей сумме.Когда все рабочие закончены, вычисляется и печатается среднее значение.

import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

class Worker implements Runnable {

    private final AtomicInteger sum;
    private final CountDownLatch latch;

    public Worker(AtomicInteger sum, CountDownLatch latch) {
        this.sum = sum;
        this.latch = latch;
    }

    @Override
    public void run() {
        Random random = new Random();

        try {
            // Sleep a random length of time from 5-10s
            Thread.sleep(random.nextInt(5000) + 5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // Compute x
        int x = random.nextInt(500);

        // Add to the shared sum
        System.out.println("Adding " + x + " to sum");
        sum.addAndGet(x);

        // This runnable is finished, so count down
        latch.countDown();
    }
}

class Program {

    public static void main(String[] args) {
        // There will be 39 workers
        final int N = 39;

        // Holds the sum of all results from all workers
        AtomicInteger sum = new AtomicInteger();
        // Tracks how many workers are still working
        CountDownLatch latch = new CountDownLatch(N);

        System.out.println("Starting " + N + " workers");

        for (int i = 0; i < N; i++) {
            // Each worker uses the shared atomic sum and countdown latch.
            Worker worker = new Worker(sum, latch);

            // Start the worker
            new Thread(worker).start();
        }

        try {
            // Important: waits for all workers to finish.
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // Compute the average
        double average = (double) sum.get() / (double) N;

        System.out.println("    Sum: " + sum.get());
        System.out.println("Workers: " + N);
        System.out.println("Average: " + average);
    }

}

Вывод должен выглядеть примерно так:

Starting 39 workers
Adding 94 to sum
Adding 86 to sum
Adding 454 to sum
...
...
...
Adding 358 to sum
Adding 134 to sum
Adding 482 to sum
    Sum: 10133
Workers: 39
Average: 259.8205128205128

Редактировать: Простодля забавы, вот пример использования ExecutorService, Callable и Future.

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledThreadPoolExecutor;

class Worker implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        Random random = new Random();

        // Sleep a random length of time, from 5-10s
        Thread.sleep(random.nextInt(5000) + 5000);

        // Compute x
        int x = random.nextInt(500);
        System.out.println("Computed " + x);

        return x;
    }

}

public class Program {

    public static void main(String[] args) {
        // Thread pool size
        final int POOL_SIZE = 10;

        // There will be 39 workers
        final int N = 39;

        System.out.println("Starting " + N + " workers");

        // Create the workers
        Collection<Callable<Integer>> workers = new ArrayList<Callable<Integer>>(N);

        for (int i = 0; i < N; i++) {
            workers.add(new Worker());
        }

        // Create the executor service
        ExecutorService executor = new ScheduledThreadPoolExecutor(POOL_SIZE);

        // Execute all the workers, wait for the results
        List<Future<Integer>> results = null;

        try {
            // Executes all tasks and waits for them to finish
            results = executor.invokeAll(workers);
        } catch (InterruptedException e) {
            e.printStackTrace();
            return;
        }

        // Compute the sum from the results
        int sum = 0;

        for (Future<Integer> future : results) {
            try {
                sum += future.get();
            } catch (InterruptedException e) {
                e.printStackTrace(); return;
            } catch (ExecutionException e) {
                e.printStackTrace(); return;
            }
        }

        // Compute the average
        double average = (double) sum / (double) N;

        System.out.println("         Sum: " + sum);
        System.out.println("     Workers: " + N);
        System.out.println("     Average: " + average);
    }

}

Выходдолжно выглядеть так:

Starting 39 workers
Computed 419
Computed 36
Computed 338
...
...
...
Computed 261
Computed 354
Computed 112
         Sum: 9526
     Workers: 39
     Average: 244.25641025641025
1 голос
/ 18 октября 2011

Вы можете заявить о себе java.util.concurrent.Future и всем связанным с ним вещам, таким как ThreadPools, Executors и т. Д. Тизер: Future - это поток с возвращаемым значением.

0 голосов
/ 18 октября 2011

используйте ExecutorService и отправьте ему каждое задание (как Callable)

вы получите будущее за каждую отправленную задачу

List<Future<ResultType>> results = exec.invokeAll(tasks);//tasks is a set of Callable<ResultType>
//invokeAll blocks untill all tasks are finished
for(Future<ResultType> f:results){
    ResultType x=f.get();//loop over Futures to get the result
    //do something with x
}
...