Есть ли действительно условие гонки в этом многопоточном коде Java?

1

Я видел фрагмент кода в этом вопросе, который я не мог понять (скорее всего, из-за того, что я новичок в этой области). В этом вопросе говорится о "состоянии очевидной гонки, где иногда продюсер заканчивает, сигнализирует об этом, а ConsumerWorkers останавливается, прежде чем потреблять все в очереди".

  • По моему мнению, "isRunning" будет установлен на потребителя только после того, как производитель решит не добавлять больше элементов в очередь. Таким образом, если потребительский поток видит isRunning как FALSE, а затем видит, что inputQueue пуст, тогда нет никакой возможности добавить что-то в очередь в будущем. Obviosuly, я ошибаюсь и что-то не хватает, так как никто, кто ответил на этот вопрос, сказал, что сценарий вопроса невозможен. Итак, может ли кто-нибудь объяснить, какая последовательность событий вызывает это состояние гонки?

  • На самом деле, я вижу проблему с чем-то другим. Например, если несколько потребительских потоков увидели, что производитель isRunning, и говорят, что очередь имеет ОДИН элемент, многие потоки могут войти в заблокированный "take". Если производитель STOPS теперь, пока один поток выйдет из "take", другие потоки блокируются на "взятии" навсегда. Интересно, что никто, кто ответил на этот вопрос, не указал на эту проблему. Итак, мое понимание этого также, вероятно, ошибочно?!

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

public class ConsumerWorker implements Runnable{

private BlockingQueue<Produced> inputQueue;
private volatile boolean isRunning = true;

public ConsumerWorker(BlockingQueue<Produced> inputQueue) {
    this.inputQueue = inputQueue;
}

@Override
public void run() {
    //worker loop keeps taking en element from the queue as long as the producer is still running or as 
    //long as the queue is not empty:
    while(isRunning || !inputQueue.isEmpty()) {
        System.out.println("Consumer "+Thread.currentThread().getName()+" START");
        try {
            Object queueElement = inputQueue.take();
            //process queueElement
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

//this is used to signal from the main thread that he producer has finished adding stuff to the queue
public void setRunning(boolean isRunning) {
    this.isRunning = isRunning;
}
Теги:
multithreading
race-condition
synchronization
producer-consumer

2 ответа

1
Лучший ответ

Я думаю, что OP исходного вопроса, вероятно, означал

while(isRunning && !inputQueue.isEmpty()) 

а не

while(isRunning || !inputQueue.isEmpty())

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

(*) и почему-то предполагает, что очередь никогда не будет пустой.

  • 1
    У меня была такая же мысль. И я проверил «правки» по этому вопросу, чтобы увидеть, изменил ли ОП вопрос и изменил ли оператор. Но он не имел. Этот оператор всегда был || И никто в этом вопросе не сомневался в этом!
  • 0
    это, вероятно, потому что это не было необходимо, так как оба выражения неверны, решение было необходимо. Простой надзор на самом деле.
0

Вы правы в обоих вопросах. Да && правильный, а || - нет. Что касается второго вопроса, ответы заключались в использовании poison pill или таймаута, в обоих направлениях решения проблемы.

Что касается меня, я бы создал новый класс синхронизации, который объединяет как очередь, так и переменную isRunning, так что изменение isRunning вызывает исключение в take(), что сигнализирует о завершении работы.

  • 0
    Мне действительно понравился ответ @PeterLawrey. Но это я.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню