1

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

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

  2. Фактически, я вижу проблему с чем-то еще. Например, если несколько потребительских потоков увидели, что производитель isRunning, и говорят, что очередь имеет ОДИН элемент, многие потоки могут войти в заблокированный «take». Если производитель STOPS теперь, а один поток выйдет из «take», , остальные потоки будут заблокированы на «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; 
} 

ответ

1

Я думаю, что ОП оригинального вопроса, вероятно, имел в виду

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

, а не

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

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

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

+1

У меня была такая же мысль. И я проверил «редактирование» по этому вопросу, чтобы узнать, изменил ли ОП вопрос и модифицировал оператор. Но, он не имел. Этот оператор всегда был ||. И никто в этом не сомневался! – brainOverflow

+0

это, вероятно, потому, что это не было необходимо, так как оба выражения ошибочны, было необходимо решение. Простой контроль действительно. – didierc

0

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

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

+0

Мне действительно понравился ответ @PeterLawrey лучшим. Но это я. – brainOverflow

Смежные вопросы