H2O生成器java线程锁,可重入锁

问题描述 投票:0回答:2

我想通过三线连续产生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.util.concurrent.locks.ReentrantLock中的java.lang.IllegalMonitorStateException $ Sync.tryRelease(ReentrantLock.java:151)at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer。 java:1261)at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457)at com.threads.reentrantlock.consumerproducer.H2OProducer.hydrogenProducer(H2OProducer.java:56)at com.threads.reentrantlock.consumerproducer java.lang.Thread.run中的.H2OProducer.lambda $ 2(H2OProducer.java:29)(Thread.java:745)

java multithreading locking producer-consumer
2个回答
2
投票

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

我假设你想在生产H之前生产两个Os。两个Hs是由同一个线程还是两个不同的线程产生并不重要。我用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
投票

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

这两项任务完全相同,只是Hydrogen_Producer只生产氢气和氧气_产品只会产生氧气。这两个任务都包含一个无限循环,只有在调用Stop条目时才会中断。在调用Stop时,退出命令命令循环退出。我们还希望能够从每个生成器获取数据,以便通过接受Get_Element条目并传递生成的元素来处理该角色。显然,我们将收到一个Stop入场调用,一个Get_Element入口调用或没有入口调用。 Select命令允许我们的程序处理Stop或Get_Element,而不对其中一个进行处理。什么都没有调用条目会发生什么?生产者在select块中等待一个要调用的条目,从而使执行与调用者同步。

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

-----------------------------------------------------------------------
-- 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条目。如果生产者没准备好会怎么样?毕竟,每个生产者在生产其元素时会经历一个随机的延迟。结果是,在处理每个条目调用之前,挂起调用(Consuming)任务(Three_Task_H2O)。每当产生另外20个水分子时,输出关于水生产进展的信息。当产生1000个水分子时,循环终止并且调用所有三个任务的停止条目,按顺序终止每个任务。

© www.soinside.com 2019 - 2024. All rights reserved.