使用Java的监视器如何解决生产者和消费者问题?

时间:2018-12-06 15:49:53

标签: java multithreading producer-consumer

我有一个线程子类,它代表到达公职的人,还有一个线程子类,代表个案工作者。

每次到达一个人时,都会输入该人的名字。办公室有2个入口,入境者可在其中输入姓名。等候室最多只能容纳10人。如果还有更多,他们必须等到有空位为止。

个案工作者呼叫输入的姓名。有2个个案工作者。当然,只有输入名称并按输入顺序(如队列)调用它们,才能调用名称。当所有的名字都调出来后,案例工作者必须等到更多的人到来。

到达者和案例工作者的线程每次都在随机数秒(从1到10秒)内进入睡眠状态。

我还有一个公共类,该类包含一个带有名称列表的arraylist作为方法,以及一些用于调用名称和输入名称的方法。它还包含用于解决此问题的append和take方法(java的监视器)。

到目前为止,这是我的代码:

import java.util.Random;

public class ThreadClass_Arrivals extends Thread {

    private CommonClass commonClass;
    public int threadID;

    public ThreadClass_Arrivals(CommonClass commonClass, int threadID) {
        this.commonClass = commonClass;
        this.threadID = threadID;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {

            // time interval before a name is entered - the thread sleep between 1-10 seconds every time
            Random random = new Random();
            int randomNumber = random.nextInt(10) + 1;
            int numberInThousand = randomNumber * 1000;
            try {
                Thread.sleep(numberInThousand);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            commonClass.enterName(commonClass.namesList().get(commonClass.nameEnteredIndex), this.threadID);

            // java monitor
            commonClass.append((char) commonClass.nameEnteredIndex);
        }
    }
}

import java.util.Random;

public class ThreadClass_Caseworkers extends Thread {

    private CommonClass commonClass;
    public int threadID;

    public ThreadClass_Caseworkers(CommonClass commonClass, int threadID) {
        this.commonClass = commonClass;
        this.threadID = threadID;
    }

    @Override
    public void run() {

        for (int i = 0; i < 20; i++) {

            // Time interval before a name is called - The thread sleep between 1-10 seconds every time
            Random random = new Random();
            int randomNumber = random.nextInt(10) + 1;
            int numberInThousand = randomNumber * 1000;
            try {
                Thread.sleep(numberInThousand);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // java monitor
            commonClass.take((char) commonClass.nameEnteredIndex);

            commonClass.callAName(this.threadID, commonClass.nameCalledIndex);

        }
    }

}

import java.util.ArrayList;

public class CommonClass {

    // java monitor
    int N = 10;
    char[] buffer = new char[N];
    int nextin, nextout, count;

    public int nameEnteredIndex;
    public int nameCalledIndex;

    // names list with 20 names
    public ArrayList<String> namesList() {
        ArrayList<String> names = new ArrayList<>();
        names.add("Hans");
        names.add("Jens");
        names.add("Rasmus");
        names.add("Kasper");
        names.add("Niels");
        names.add("Torben");
        names.add("Peter");
        names.add("Michael");
        names.add("Lars");
        names.add("Anders");
        names.add("Bo");
        names.add("Klaus");
        names.add("Ib");
        names.add("Kevin");
        names.add("Oscar");
        names.add("Nicolaj");
        names.add("Alexander");
        names.add("Morten");
        names.add("Carsten");
        names.add("Jakob");

        return names;
    }

    public synchronized void enterName(String name, int threadID) {

        if (threadID == 0) {
            System.out.println("Name-entered (entrance1): " + name);
        } else if (threadID == 1) {
            System.out.println("Name-entered (entrance2): " + name);
        }
        nameEnteredIndex++;
    }

    public synchronized void callAName(int threadID, int index) {
        if (threadID == 0) {
            System.out.println("Name called (caseworker1): " + namesList().get(index));
        } else if (threadID == 1) {
            System.out.println("Name called (caseworker2): " + namesList().get(index));
        }
        nameCalledIndex++;
    }

    // java monitor
    public synchronized void append(char x) {
        if (count == N) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        buffer[nextin] = x;
        nextin = (nextin + 1) % N;
        count++;
        notifyAll();
    }

    // java monitor
    public synchronized void take(char x) {
        if (count == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        x = buffer[nextout];
        nextout = (nextout + 1) % N;
        count--;
        notifyAll();
    }
}

public class MainApp {

    public static void main(String[] args) {

        // commonclass
        CommonClass commonClass = new CommonClass();

        // Thread - arrivals
        ThreadClass_Arrivals threadClass_arrival1 = new ThreadClass_Arrivals(commonClass, 0);
        ThreadClass_Arrivals threadClass_arrival2 = new ThreadClass_Arrivals(commonClass, 1);
        threadClass_arrival1.start();
        threadClass_arrival2.start();

        // Thread - caseworkers
        ThreadClass_Caseworkers threadClass_caseworker1 = new ThreadClass_Caseworkers(commonClass, 0);
        ThreadClass_Caseworkers threadClass_caseworker2 = new ThreadClass_Caseworkers(commonClass, 1);
        threadClass_caseworker1.start();
        threadClass_caseworker2.start();
    }

}

问题在于,即使在列表中有20个名称,也要在输入某些名称之前调用一些名称,而且还会得到ArrayOutOfBounceException,并且有一个循环从2个线程子类中的列表中检索20个名称。

任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:1)

您提出的问题的细节很复杂,但是我可以帮助您了解生产者/消费者问题中如何使用java的监视器(wait,notify,notifyAll)。

这是需要完成的工作队列的中心。此队列(或其他有序数据结构)需要具有两个同步的方法:弹出作业,然后推送作业

synchronized void push(T) {
  myInternalQueue.push(T);
  notify();
}

synchronized T pop {
  if (myInternalQueue.length() == 0) { // you can also use while here, if a thread could wake up and the queue might still be empty
    wait();
  }
  return myInternalQueue.pop();
}

Consumer线程调用pop以获取下一个作业。如果没有下一个作业,则Producer线程将另一个作业推入队列并调用notify时,线程等待被唤醒。请注意,在我给出的示例中,由于两种方法已经同步,因此在pop和push中不需要内部同步块

答案 1 :(得分:0)

这是使用Ada编程的非常相似的问题的解决方案。 在此解决方案中,生产者和消费者一直运行,直到主要任务发出停止信号。以这种方式,客户数量是由时间而不是简单的循环计数来控制的。 每个生产者都通过他们进入的门为客户命名,以便我们可以看到两个生产者都在工作。在处理客户时,每个业务员都将标识每个客户。 “等候室”实现为包含10个元素的同步队列。同步队列处理所有数据同步问题,包括队列满时挂起生产者和队列空时挂起文员(消费者)。

Ada在程序包中定义程序模块。每个软件包必须具有一个规范,该规范定义了模块的公共接口和私有接口。软件包通常还具有定义模块行为的主体。主过程创建生产者任务类型和消费者任务类型的实例,这些实例立即开始运行。然后主机延迟(睡眠)80秒,并为每个任务调用“停止”条目。

-----------------------------------------------------------------------
-- Services package
--
-- This package defines a synchronized bounded queue holding the
-- names of customers in a service industry.
--
-- It also defines a producer task type which simulates customers
-- entering a waiting room (the synchronized queue) and a consumer
-- task type simulating a person serving people in the waiting
-- room.
-----------------------------------------------------------------------


package Simulation is
   type Door_Type is (Front, Back);
   type Clerk_Type is(Herman, Jill);

   task type Producer(Door : Door_Type) is
      Entry Stop;
   end Producer;

   task type Consumer(Clerk : Clerk_Type) is
      Entry Stop;
   end Consumer;

end Simulation;

包装体为:

with Ada.Containers.Synchronized_Queue_Interfaces;
with Ada.Containers.Bounded_Synchronized_Queues;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Numerics.Float_Random; use Ada.Numerics.Float_Random;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers; use Ada.Containers;


package body Simulation is

   package Queue_Interface is new Ada.Containers.Synchronized_Queue_Interfaces
     (Unbounded_String);
   use Queue_Interface;

   package Bounded_Queue is new Ada.Containers.Bounded_Synchronized_Queues
     (Queue_Interface, 10);
   use Bounded_Queue;

   Seed : Generator;

   Waiting_Room : Bounded_Queue.Queue;
   --------------
   -- Producer --
   --------------

   task body Producer is
      Name  : Unbounded_String;
      Count : Positive := 1;
      Sleep : Float;
   begin
      loop
         select
            accept Stop;
            exit;
         else
            Name := To_Unbounded_String(Door'Image & " " & Count'Image);
            Count := Count + 1;
            Waiting_Room.Enqueue(Name);
            Put_Line("                " & To_String(Name) & " entered waiting room.");
            Sleep := (Random(Seed) * 9.0) + 1.0;
            delay Duration(Sleep);
         end select;
      end loop;
      Put_Line("                 Producer " & Door'Image & " is finished.");
   end Producer;

   --------------
   -- Consumer --
   --------------

   task body Consumer is
      Name  : Unbounded_String;
      Sleep : Float;
   begin
      Loop
         select
            accept Stop;
            exit;
         else
            select
               Waiting_Room.Dequeue(Name);
               Put_Line(Clerk'Image & " is now serving " & To_String(Name));
               Sleep := (Random(Seed) * 9.0) + 1.0;
               delay duration(sleep);
            or
               delay 0.001;
            end select;

         end select;
      end loop;
      while Waiting_Room.Current_Use > 0 loop
         select
            Waiting_Room.Dequeue(Name);
            Put_Line(Clerk'Image & " is now serving " & To_String(Name));
            Sleep := (Random(Seed) * 9.0) + 1.0;
            delay duration(sleep);
         else
            exit;
         end select;
      end loop;
      Put_Line("                Clerk " & Clerk'Image & " is finished.");
   end Consumer;

begin
   Reset(Seed);

end Simulation;

该程序的主要过程是:

with Simulation; use Simulation;

procedure Main is
   P1 : Producer(Front);
   P2 : Producer(Back);
   C1 : Consumer(Herman);
   C2 : Consumer(Jill);

begin
   delay 80.0;
   P1.Stop;
   P2.Stop;
   C1.Stop;
   C2.Stop;
end Main;

最后,执行此程序的输出为:

                FRONT  1 entered waiting room.
                BACK  1 entered waiting room.
HERMAN is now serving FRONT  1
JILL is now serving BACK  1
                FRONT  2 entered waiting room.
HERMAN is now serving FRONT  2
                FRONT  3 entered waiting room.
JILL is now serving FRONT  3
                BACK  2 entered waiting room.
HERMAN is now serving BACK  2
                FRONT  4 entered waiting room.
HERMAN is now serving FRONT  4
                BACK  3 entered waiting room.
JILL is now serving BACK  3
                BACK  4 entered waiting room.
JILL is now serving BACK  4
                FRONT  5 entered waiting room.
HERMAN is now serving FRONT  5
                FRONT  6 entered waiting room.
                FRONT  7 entered waiting room.
JILL is now serving FRONT  6
                BACK  5 entered waiting room.
HERMAN is now serving FRONT  7
                FRONT  8 entered waiting room.
                FRONT  9 entered waiting room.
JILL is now serving BACK  5
HERMAN is now serving FRONT  8
                BACK  6 entered waiting room.
                FRONT  10 entered waiting room.
HERMAN is now serving FRONT  9
JILL is now serving BACK  6
                BACK  7 entered waiting room.
HERMAN is now serving FRONT  10
                FRONT  11 entered waiting room.
HERMAN is now serving BACK  7
                BACK  8 entered waiting room.
JILL is now serving FRONT  11
HERMAN is now serving BACK  8
                FRONT  12 entered waiting room.
HERMAN is now serving FRONT  12
                BACK  9 entered waiting room.
JILL is now serving BACK  9
                FRONT  13 entered waiting room.
JILL is now serving FRONT  13
HERMAN is now serving BACK  10
                BACK  10 entered waiting room.
                BACK  11 entered waiting room.
                FRONT  14 entered waiting room.
HERMAN is now serving BACK  11
                BACK  12 entered waiting room.
HERMAN is now serving FRONT  14
JILL is now serving BACK  12
                BACK  13 entered waiting room.
                FRONT  15 entered waiting room.
HERMAN is now serving BACK  13
JILL is now serving FRONT  15
                BACK  14 entered waiting room.
JILL is now serving BACK  14
                FRONT  16 entered waiting room.
JILL is now serving FRONT  16
                BACK  15 entered waiting room.
JILL is now serving BACK  15
                 Producer FRONT is finished.
                 Producer BACK is finished.
                Clerk HERMAN is finished.
                Clerk JILL is finished.