Как заставить один поток Java ждать результата другого потока? - PullRequest
5 голосов
/ 07 марта 2011

Мне часто нужно, чтобы поток ожидал результата другого потока.Похоже, в java.util.concurrent должна быть некоторая поддержка, но я не могу ее найти.

Exchanger очень близок к тому, о чем я говорю, но этодвунаправленный.Я только хочу, чтобы поток A ожидал в потоке B, но не заставлял обоих ждать друг друга.

Да, я знаю, что могу использовать CountDownLatch или Semaphore или Thread.wait (), а затем управлять результатомвычислений сам, но кажется, что мне где-то не хватает удобного класса.

Чего мне не хватает?

ОБНОВЛЕНИЕ

// An Example which works using Exchanger
// but you would think there would be uni-directional solution
protected Exchanger<Integer> exchanger = new Exchanger<Integer>();

public void threadA() {
    // perform some computations
    int result = ...;

    exchanger.exchange(result);
}


public void threadB() {

    // retrieve the result of threadA
    int resultOfA = exchanger.exchange(null);
}

Ответы [ 7 ]

7 голосов
/ 07 марта 2011

Вы ищете Future<T>?Это нормальное представление задачи, которая (как правило) была отправлена ​​в рабочую очередь, но, возможно, еще не выполнена.Вы можете узнать его состояние завершения, заблокировать до его завершения и т. Д.

Посмотрите на ExecutorService для нормального способа получения фьючерсов.Обратите внимание, что это направлено на получение результата отдельной задачи , а не на ожидание завершения thread .Конечно, один поток может выполнить много задач за время своей жизни - в этом весь смысл пула потоков.

5 голосов
/ 07 марта 2011

Пока что кажется, что BlockingQueue может быть лучшим решением, которое я нашел.

например.

BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(1);

Ожидающий поток вызовет queue.take() дождаться результата, и производящая очередь вызовет queue.add() для отправки результата.

4 голосов
/ 07 марта 2011

JDK не предоставляет удобный класс, который обеспечивает именно ту функциональность, которую вы ищете. Однако на самом деле довольно просто написать небольшой служебный класс, чтобы сделать это.

Вы упомянули CountDownLatch и ваши предпочтения относительно него, но я все равно рекомендую посмотреть на него. Вы можете создать небольшой служебный класс (если хотите, «синхронизатор значений»):

public class OneShotValueSynchronizer<T> {
    private volatile T value;
    private final CountDownLatch set = new CountDownLatch(1);

    public T get() throws InterruptedException {
        set.await();
        return value;
    }

    public synchronized void set(T value) {
        if (set.getCount() > 0) {
            this.value = value;
            set.countDown();
        }
    }

    // more methods if needed
}
0 голосов
/ 04 сентября 2017

Начиная с Java 8 вы можете использовать CompletableFuture<T>. Поток A может ожидать результат, используя блокирующий метод get(), тогда как поток B может передавать результат вычисления, используя complete().

Если поток B встречает исключение при вычислении результата, он может сообщить об этом потоку A, вызвав completeExceptionally().

0 голосов
/ 07 марта 2011

Для этого вы можете использовать java.util.concurrent.CountDownLatch.

http://download.oracle.com/javase/6/docs/api/java/util/concurrent/CountDownLatch.html

Пример:

CountDownLatch latch = new CountDownLatch(1);

// thread one
// do some work
latch.countDown();

// thread two
latch.await();
0 голосов
/ 07 марта 2011

Недавно у меня возникла та же проблема, я пытался использовать Future, а затем CountdownLatch, но остановился на обменнике. Предполагается, что они позволяют двум потокам обмениваться данными, но нет причины, по которой один из этих потоков не может просто пропустить ноль.

В конце концов, я думаю, что это было самое чистое решение, но это может зависеть от того, чего именно вы пытаетесь достичь.

0 голосов
/ 07 марта 2011

Что неудобно при использовании Thread.join()?

...