2015-10-08 3 views
2

Я получил пример от here. Но там они просто реализуют этот пример только один за другим. Среднее первое целое число для создания, а затем путайте и останавливайте программу.Продюсер и потребитель, использующий блокировку в Java

Вот оригинальный пример:

class ProducerConsumerImpl { 
    // producer consumer problem data 
    private static final int CAPACITY = 10; 
    private final Queue queue = new LinkedList<>(); 
    private final Random theRandom = new Random(); 

    // lock and condition variables 
    private final Lock aLock = new ReentrantLock(); 
    private final Condition bufferNotFull = aLock.newCondition(); 
    private final Condition bufferNotEmpty = aLock.newCondition(); 

    public void put() throws InterruptedException { 
     aLock.lock(); 
     try { 
      while (queue.size() == CAPACITY) { 
       System.out.println(Thread.currentThread().getName() 
         + " : Buffer is full, waiting"); 
       bufferNotEmpty.await(); 
      } 

      int number = theRandom.nextInt(); 
      boolean isAdded = queue.offer(number); 
      if (isAdded) { 
       System.out.printf("%s added %d into queue %n", Thread 
         .currentThread().getName(), number); 

       // signal consumer thread that, buffer has element now 
       System.out.println(Thread.currentThread().getName() 
         + " : Signalling that buffer is no more empty now"); 
       bufferNotFull.signalAll(); 
      } 
     } finally { 
      aLock.unlock(); 
     } 
    } 

    public void get() throws InterruptedException { 
     aLock.lock(); 
     try { 
      while (queue.size() == 0) { 
       System.out.println(Thread.currentThread().getName() 
         + " : Buffer is empty, waiting"); 
       bufferNotFull.await(); 
      } 

      Integer value = queue.poll(); 
      if (value != null) { 
       System.out.printf("%s consumed %d from queue %n", Thread 
         .currentThread().getName(), value); 

       // signal producer thread that, buffer may be empty now 
       System.out.println(Thread.currentThread().getName() 
         + " : Signalling that buffer may be empty now"); 
       bufferNotEmpty.signalAll(); 
      } 

     } finally { 
      aLock.unlock(); 
     } 
    } 
} 

После того, что я изменить код и сделать его работу, как, First 10 Produce, затем 10 Потребляйте, цикл работает до тех пор, пока программа завершается. Вот мой модифицированный код:

class ProducerConsumerImpl { 
    // producer consumer problem data 
    private static final int CAPACITY = 10; 
    private final Queue<Integer> queue = new LinkedList<>(); 
    private final Random theRandom = new Random(); 

    // lock and condition variables 
    private final Lock aLock = new ReentrantLock(); 
    private final Condition bufferNotFull = aLock.newCondition(); 
    private final Condition bufferNotEmpty = aLock.newCondition(); 

    public void put() throws InterruptedException { 
     aLock.lock(); 
     try { 
      while(true){ 
       while (queue.size() == CAPACITY) { 
        System.out.println(Thread.currentThread().getName() 
          + " : Buffer is full, waiting"); 

        bufferNotEmpty.await(); 
       } 

       int number = theRandom.nextInt(); 
       boolean isAdded = queue.offer(number); 
       if (isAdded) { 
        System.out.printf("%s added %d into queue %n", Thread 
          .currentThread().getName(), number); 
       } 
       bufferNotFull.signalAll(); 
      } 

     } finally { 
      aLock.unlock(); 
     } 
    } 

    public void get() throws InterruptedException { 
     aLock.lock(); 
     try { 
      while(true){ 
       while (queue.size() == 0) { 
        System.out.println(Thread.currentThread().getName() 
          + " : Buffer is empty, waiting"); 
        bufferNotFull.await(); 
       } 

       Integer value = (Integer)queue.poll(); 
       if (value != null) { 
        System.out.printf("%s consumed %d from queue %n", Thread 
          .currentThread().getName(), value); 
       } 
       bufferNotEmpty.signalAll(); 
      } 

     } finally { 
      aLock.unlock(); 
     } 
    } 
} 

После модификации Это прекрасно работает, производя 10 случайных чисел, а затем потребляя эти целые числа снова и снова, пока программы существуют/прекратить. Но поскольку я не мастер/эксперт по параллелизму.

Так что я хочу задать есть ли какие-либо проблемы в моем модифицированном коде?

У меня возникли проблемы с размещением на моем bufferNotFull.signalAll(); и bufferNotEmpty.signalAll();, потому что каждый раз он мог уведомлять об ожидающем потоке.

Если есть проблема, то как я могу решить? или Если все в порядке, то, пожалуйста, также очистите мою путаницу bufferNotFull.signalAll(); and bufferNotEmpty.signalAll(); placement, because that could notify waiting thread each time..

Примечание: Это просто получить и установить код (производящими и потребляющими)

Выход измененного кода:.

PRODUCER added 1062016967 into queue 
PRODUCER added 1204607478 into queue 
PRODUCER added 1865840177 into queue 
PRODUCER added -1279321362 into queue 
PRODUCER added -190570442 into queue 
PRODUCER added -1344361101 into queue 
PRODUCER added 609239106 into queue 
PRODUCER added -1480451794 into queue 
PRODUCER added 1905208395 into queue 
PRODUCER added -420578734 into queue 
PRODUCER : Buffer is full, waiting 
CONSUMER consumed 1062016967 from queue 
CONSUMER consumed 1204607478 from queue 
CONSUMER consumed 1865840177 from queue 
CONSUMER consumed -1279321362 from queue 
CONSUMER consumed -190570442 from queue 
CONSUMER consumed -1344361101 from queue 
CONSUMER consumed 609239106 from queue 
CONSUMER consumed -1480451794 from queue 
CONSUMER consumed 1905208395 from queue 
CONSUMER consumed -420578734 from queue 
CONSUMER : Buffer is empty, waiting 
PRODUCER added 1917580670 into queue 
so on......... 

Edited Подумав в производительности я решил добавить если условие в обоих, put и get метод до signalall(); заявление. (Я думаю, что это может повысить производительность даже 0.000000Что-то) Но прерывать ожидание(); может быть deadlock(); Любая помощь?

Для Производитель:

if(queue.size() == CAPACITY){ 
bufferNotFull.signalAll(); 
} 

Для потребителя:

if(queue.size() == 0){ 
bufferNotEmpty.signalAll(); 
} 
+1

Основываясь на выходе вашей программы, мне кажется, что она делает именно то, что она должна делать: производитель помещает вещи в очередь, потребитель берет их. Вы можете получить более «смешанные» результаты, если вы ставите циклы 'while (true)' вне циклов 'try' /' finally'. Затем вместо просмотра производителя/производителя/производителя/потребителя/потребителя/потребителя вы можете увидеть производителя/потребителя/производителя/потребителя/производителя/потребителя. –

+1

Оригинальный код выглядит более естественным, чем ваша модификация, именно из-за комментария Брайана. Но исходный код использует путаное имя для переменных условия: 'bufferNotFull' для проверки того, что очередь ** не пуста ** и' bufferNotEmpty' для проверки того, что очередь ** не заполнена **. Обратите внимание: при попытке уменьшить время сигнализации (в конце вашего вопроса) вы используете естественный смысл имен переменных: 'bufferNotFull' с' queue.size() == CAPACITY' и 'bufferNotEmpty' с' queue.size() == 0'. – Tsyvarev

+0

@ Цыварев спасибо за ваш комментарий. Хорошо понял. нет ли соглашения concurreny/deadlock/hack/thread-lock/thread-safe? – UnKnown

ответ

1

Модифицированный код ОК.

Что касается возможных оптимизации производительности посредством пропуска .singnalAll() вызовов при их не нужно: достаточно испускать сигнал только тогда, когда состояние изменяется от false до true.

Для производителя вы можете использовать:

if(isAdded && queue.size() == 1) { 
    /* 
    * Element has been *actually added* *into empty queue* 
    * (previously .size() = 0), thus *queue become non-empty*. 
    */ 
    bufferNotFull.signalAll(); 
} 

Для потребителя вы можете использовать:

if(value && queue.size() == CAPACITY - 1) { 
    /* 
    * Element has been *actually consumed* *from full queue* 
    * (previousely .size() = CAPACITY), thus *queue become non full*. 
    */ 
    bufferNotEmpty.signalAll(); 
} 

Обратите внимание, что в этом случае вы не устранить ненужные уведомления для официантов (например., потребитель может ждать элемент только для пустой очереди, таким образом, только добавление первого элемента пробудит его). Вместо этого вы удаляете вызовы на .notifyAll(), когда известно, что нить не ждет его (например, потребитель не может ждать в непустой очереди).

+0

Почему вы использовали 'queue.size() == 1'? это будет уведомлять только за один раз. и после этого условия не разблокированы. Я уверен, что это может вызвать DeadLock. – UnKnown

+0

Любой потребитель может ждать только в очереди в ** пустом состоянии ** (размер равен '0'). 'queue.size() == 1' будет уведомлять * каждый раз * при преобразовании очереди ** из пустого состояния ** (размер' 0') ** в непустое состояние ** (размер '1'). Таким образом, мы никогда не пропускаем сигналы, когда они нужны. – Tsyvarev

+0

Я немного смущен, предположим, что продюсер будет уведомлять об этом в 'queue.size() == 1' после этого блокировки не будет выпущен ... так что продюсер будет производить снова .., а затем размер будет до 1 .. Итак, уведомлять потребителя после размера до 1? – UnKnown

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