H2O производитель блокировка потоков Java, блокировка входа - PullRequest
0 голосов
/ 19 марта 2019

Я хочу производить h2o непрерывно тремя нитями: первая нить выдаст h, вторая - ч, а третья - o. Как я могу сделать это с замком, потребитель производитель

        package com.threads.reentrantlock.consumerproducer;

        import java.util.concurrent.locks.Condition;
        import java.util.concurrent.locks.Lock;
        import java.util.concurrent.locks.ReentrantLock;

        public class H2OProducer {
            static Lock lock = new ReentrantLock(true);
            static Condition condition = lock.newCondition();

            public static void main(String[] args) {
                try {
                    Thread h1 = new Thread(() -> {
                        try {
                            hydrogenProducer();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    });
                    Thread h2 = new Thread(() -> {
                        try {
                            hydrogenProducer();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    });
                    Thread o = new Thread(() -> {
                        try {
                            hydrogenProducer();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    });
                    h1.start();
                    h2.start();
                    o.start();

                    try {
                        h1.join();
                        h2.join();
                        o.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } catch (Exception e) {
                }

            }

            public static void hydrogenProducer() throws InterruptedException {
                try {
                    lock.lock();
                        System.out.println("h");
                condition.signalAll();
            } finally {
                lock.unlock();
            }

        }

        public static void oxygenProducer() throws InterruptedException {
            try {
                lock.lock();
                System.out.println("o");
                    condition.signalAll();
                } finally {
                    lock.unlock();
                }
            }
        }

Что я делаю не так

Исключение в теме "Нить-2" ч java.lang.IllegalMonitorStateException в java.util.concurrent.locks.ReentrantLock $ Sync.tryRelease (ReentrantLock.java:151) в java.util.concurrent.locks.AbstractQueuedSynchronizer.release (AbstractQueuedSynchronizer.java:1261) в java.util.concurrent.locks.ReentrantLock.unlock (ReentrantLock.java:457) на com.threads.reentrantlock.consumerproducer.H2OProducer.hydrogenProducer (H2OProducer.java:56) на com.threads.reentrantlock.consumerproducer.H2OProducer.lambda $ 2 (H2OProducer.java:29) at java.lang.Thread.run (Thread.java:745)

Ответы [ 2 ]

2 голосов
/ 19 марта 2019

Вы сигнализируете о состоянии, но соответствующего ожидания нет.Более того, есть опечатка - вызов hydrogenProducer() из обоих потоков (Thread o и Thread h)

Я предполагаю, что вы хотите произвести два H с до производства O.Не имеет значения, производятся ли два H s одним и тем же потоком или двумя различными потоками.Я использовал randomSleep(), чтобы продемонстрировать эту ситуацию.

import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class H2OProducer {
    static final int H2O_COUNT = 1_000;
    static final Random rand = new Random();

    static final Lock lock = new ReentrantLock(true);
    static final Condition oxzWait = lock.newCondition();
    static final Condition hydWait = lock.newCondition();

    static volatile int hydCount = 0;

    public static void main(String[] args) {
        try {
            Thread h1 = new Thread(() -> {
                try {
                    hydrogenProducer();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            Thread h2 = new Thread(() -> {
                try {
                    hydrogenProducer();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            Thread o = new Thread(() -> {
                try {
                    oxygenProducer();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });

            h1.setName("H1-Producer");
            h2.setName("H2-Producer");
            o.setName("Ox-Producer");

            h1.start();
            h2.start();
            o.start();

            try {
                h1.join();
                h2.join();
                o.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
        }

    }

    public static void hydrogenProducer() throws InterruptedException {
        for (int i = 0; i < H2O_COUNT; i++) {
            lock.lock();
            try {
                while (hydCount == 2) {
                    hydWait.await();
                }

                hydCount++;
                System.out.println(Thread.currentThread().getName()+ ": H produced - " + i);

                if (hydCount == 2) {
                    oxzWait.signalAll();
                }
            } finally {
                lock.unlock();
            }

            randomSleep();
        }
    }

    public static void oxygenProducer() throws InterruptedException {
        for (int i = 0; i < H2O_COUNT; i++) {
            lock.lock();
            try {
                while (hydCount < 2) {
                    oxzWait.await();
                }

            hydCount = 0;
            System.out.println(Thread.currentThread().getName()+ ": O produced - " + i);
            System.out.println("");
            hydWait.signalAll();
            } finally {
                lock.unlock();
            }

            randomSleep();
        }
    } 

    public static void randomSleep() {
        int ms = rand.nextInt(500);
        try { 
            Thread.sleep(ms);
        } catch (InterruptedException ex) {
        }
    }
}

Однако, если вы хотите, чтобы каждый H продюсер произвел один H для каждой H2O композиции, тогда вы можете посмотреть на CyclicBarrier .Вы также можете выполнить цепочку потоков, если вам нужно поддерживать порядок, например, T1 -> T2 -> T3 -> T1 -> T2 -> T3 ->.

0 голосов
/ 24 марта 2019

Подход, принятый в вышеприведенных примерах Java, вынуждает HydroProducer и OxygenProducer знать, сколько элементов нужно произвести до начала производства.Другой подход к проектированию централизует знание того, когда прекращать производство, путем подсчета конечного выхода молекул воды, который соответствует подсчету конечных производственных единиц на «фабрике воды».В реальной системе управления производством решение о том, когда прекратить производство, следует централизовать, а не оставлять на усмотрение каждого отдельного компонента в системе.

Следующий пример, написанный на Аде, демонстрирует это централизованное управление.Вместо того, чтобы использовать сигналы, указывающие на то, что произошло образование водорода или кислорода, это решение фактически передает символические элементы водорода и кислорода от производителей к потребителю, который контролирует выполнение и подсчитывает производство конечных элементов.

В решении Ada используетсяМеханизм рандеву, позволяющий производителям напрямую связываться с потребителем строго контролируемым образом.

Типы задач производителя определяются в пакете с именем Elements.В отличие от Java, Ada обеспечивает разделение интерфейса и реализации.Интерфейс для пакета Elements определяется следующим образом:

   package Elements is
   type Element_Type is (Hydrogen, Oxygen);

   task type Hydrogen_Producer is
      Entry Stop;
      Entry Get_Element(Atom : out Element_Type);
   end Hydrogen_Producer;

   task type Oxygen_Producer is
      Entry Stop;
      Entry Get_Element(Atom : out Element_Type);
   end Oxygen_Producer;

   end Elements;

Определение типа в верхней части спецификации интерфейса Elements определяет тип данных с именем Element_Type с двумя значениями: Hydrogen и Oxygen.Определены два типа задач: один для производства водорода и один для производства кислорода.Каждый из типов задач имеет две записи.Записи - это механизм, позволяющий одной задаче (или потоку) напрямую взаимодействовать с другой задачей.Запись Stop сообщает задаче, когда прекратить выполнение.Запись Get_Element получает экземпляр элемента, созданного задачей.

Механизм Rendezvous автоматически синхронизирует задачу, вызывающую запись, с вызываемой задачей.Реализация типов задач показывает, как осуществляется взаимодействие между задачами.

with Ada.Numerics.Float_Random; use Ada.Numerics.Float_Random;

package body Elements is
   Seed : Generator;
   -----------------------
   -- Hydrogen_Producer --
   -----------------------

   task body Hydrogen_Producer is
      Element : constant Element_Type := Hydrogen;
   begin
      loop
         select
            accept Stop;
            exit;
         or
            accept Get_Element(Atom : out Element_Type) do
               Atom := Element;
            end Get_Element;
         end select;
         delay Duration(Random(Seed) * 0.1);
      end loop;
   end Hydrogen_Producer;

   ---------------------
   -- Oxygen_Producer --
   ---------------------

   task body Oxygen_Producer is
      Element : constant Element_Type := Oxygen;
   begin
      loop
         select
            accept Stop;
            exit;
         or
            accept Get_Element(Atom : out Element_Type) do
               Atom := Element;
            end Get_Element;
         end select;
         delay Duration(Random(Seed) * 0.1);
      end loop;
   end Oxygen_Producer;
begin
   reset(Seed);
end Elements;

В теле задачи, где реализованы типы задач, объявляется переменная с именем seed.Переменная seed является экземпляром типа Generator, определенного в пакете Ada.Numerics.Float_Random.Эта переменная будет содержать начальное число случайных чисел, используемое для генерации случайных задержек для задач производителя.Начальное число инициализируется в нижней части файла задачи перед тем, как начнется выполнение любой из задач производителя.

Эти две задачи в точности совпадают, за исключением того, что Hydrogen_Producer производит только Hydrogen, а Oxygen_Producer - только Oxygen.Обе задачи содержат бесконечный цикл, который прерывается только при вызове записи Stop.После вызова Stop выход из цикла задается командой exit .Мы также хотим иметь возможность получать данные от каждого производителя, поэтому эта роль обрабатывается путем принятия записи Get_Element и передачи созданного элемента.Очевидно, мы либо получим входной вызов Stop, входной вызов Get_Element, либо входной вызов не будет.Команда Select позволяет нашей программе обрабатывать либо Stop, либо Get_Element без предпочтения одного или другого.Что происходит, когда ни одна запись не вызывается?Производитель ожидает в блоке выбора одну из вызываемых записей, таким образом синхронизируя выполнение с вызывающей стороной.

Теперь нам нужен эквивалент «основного» метода для создания исполняемой программы.Ада позволяет программисту называть точку входа в программу как угодно.Его не нужно называть «основным».

-----------------------------------------------------------------------
-- H2O production using 2 Hydrogen tasks and 1 Oxygen task
-----------------------------------------------------------------------

with Ada.Text_IO; use Ada.Text_IO;
with Elements; use Elements;

procedure Three_Task_H2O is
   H1 : Hydrogen_Producer;
   H2 : Hydrogen_Producer;
   Oxy : Oxygen_Producer;
   New_Atom    : Element_Type;
   Water_Count : natural := 0;

begin
   while Water_Count < 1000 loop
      H1.Get_Element(New_Atom);
      H2.Get_element(New_Atom);
      Oxy.Get_Element(New_Atom);
      Water_Count := Water_Count + 1;
      if Water_Count mod 20 = 0 then
         Put_Line("Water Produced:" & Water_Count'Image);
      end if;
   end loop;
   H1.Stop;
   H2.Stop;
   Oxy.Stop;
end Three_Task_H2o;

Процедура Three_Task_H2O создает два экземпляра Hydrogen_Producer с именами H1 и H2.Он также создает экземпляр Oxygen_Producer с именем Oxy.Задачи начинают выполнение немедленно.Не существует эквивалента синтаксиса thread.start, найденного в Java.Tree_Task_H2O зацикливается, в то время как количество молекул воды меньше 1000. Каждая итерация цикла вызывает записи Get_Element для каждого производителя. Что произойдет, если производитель не готов? В конце концов, каждый производитель подвергается случайной задержке в производстве своего элемента. В результате вызывающая (потребляющая) задача (Three_Task_H2O) приостанавливается до тех пор, пока не будет обработан каждый входной вызов. Информация о ходе производства воды выводится каждый раз, когда производится еще 20 молекул воды. Когда образуется 1000 молекул воды, цикл завершается и вызываются записи Stop для всех трех задач, завершая каждую задачу по порядку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...