H2O生产者Java线程锁,可重入锁

时间:2019-03-19 09:58:04

标签: java multithreading locking producer-consumer

我想通过三个线程连续产生h2o,第一个线程将产生h,第二个线程将产生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();
                }
            }
        }

我做错了

线程“ Thread-2”中的异常h 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)     在java.lang.Thread.run(Thread.java:745)

2 个答案:

答案 0 :(得分:2)

您正在发出信号,但没有相应的等待。此外,还有一个错字-从两个线程(线程hydrogenProducer()和线程o)调用h

我假设您想在产生H之前先产生两个O。两个H是由同一个线程还是由两个不同的线程产生的,都没关系。我已经用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->。

答案 1 :(得分:0)

上面的Java示例中采用的方法迫使HydroProducer和OxygenProducer在开始生产之前就知道要生产多少产品。另一种设计方法是通过计算水分子的最终产量来集中何时停止生产的知识,这与“水厂”中最终生产项目的数量相对应。在真正的制造控制系统中,何时停止生产的决定应该集中起来,而不是留给系统中的每个单独组件。

以下用Ada编写的示例展示了这种集中控制。该解决方案并没有使用信号来指示已经发生氢气或氧气的产生,而是实际上将氢气和氧气的象征性元素从生产者传递到了消费者,后者控制执行并计算最终元素的产生。

Ada解决方案采用了Rendezvous机制,可以使生产者以严格控制的方式直接与消费者沟通。

生产者任务类型在名为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的变量。变量种子是在包Ada.Numerics.Float_Random中定义的Generator类型的实例。该变量将保存用于为生产者任务生成随机延迟的随机数种子。在任何生产者任务开始执行之前,种子将在任务文件的底部初始化。

这两个任务完全相同,除了Hydrogen_Producer仅产生氢气和Oxygen_Producer仅产生氧气。 这两个任务都包含一个无限循环,仅在调用Stop条目时才会中断。调用“停止”后,循环退出由 exit 命令命令。我们还希望能够从每个生产者那里获取数据,以便通过接受Get_Element条目并传递所产生的元素来处理角色。 显然,我们将收到一个Stop入口调用,一个Get_Element入口调用或没有入口调用。通过 Select 命令,我们的程序可以处理Stop或Get_Element,而无需选择其中一个。当两个条目都不被调用时会发生什么?生产者在选择块中等待要调用的条目之一,从而与调用者同步执行。

我们现在需要等效的“ main”方法来创建可执行程序。 Ada允许程序员使用任何名称来命名程序入口点。它不必命名为“ main”。

-----------------------------------------------------------------------
-- 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创建两个名为H1和H2的Hydrogen_Producer实例。它还创建名为Oxy的Oxygen_Producer实例。任务立即开始执行。在Java中找不到等效的thread.start语法。 当水分子的数量少于1000时,Tree_Task_H2O循环。循环的每次迭代都为每个生产者调用Get_Element条目。如果生产者还没有准备好怎么办?毕竟,每个生产者在生产其元素时都会经历随机的延迟。结果是调用(正在使用)任务(Three_Task_H2O)被挂起,直到处理每个条目调用为止。 每当产生另外20个水分子时,就会输出有关水生产进度的信息。当产生1000个水分子时,循环终止,并调用所有三个任务的“停止”条目,依次终止每个任务。