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

guz6ccqo  于 2021-06-30  发布在  Java
关注(0)|答案(2)|浏览(258)

我有一个thread子类,它代表公共办公室的到达,还有一个thread子类,它代表案例工作者。
每次有人到达时,都会输入此人的姓名。办公室有两个入口,客人可以在这里输入姓名。候车室不能超过10人。如果还有更多,他们必须等到有空位。
办案人员点名。有两名办案人员。当然,只有输入了名称并且按输入顺序(如队列)调用时,才能调用名称。当所有的人都被叫来时,办案人员必须等到更多的人来。
到达者和办案人员的线程每次睡眠时间都是随机的(从1秒到10秒)。
我还有一个公共类,它包含一个arraylist,其中有一个名称列表作为方法,还有一些用于调用名称和输入名称的方法。它还包含用于解决问题的append和take方法(java的monitor)。
以下是我目前的代码:

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();
    }

}

问题是有些名称在输入之前被调用,我也得到了arrayoutofbounceexception,即使列表中有20个名称,并且有一个循环从2个线程子类的列表中检索20个名称。
任何帮助都将不胜感激!

osh3o9ms

osh3o9ms1#

下面是一个使用ada编程的非常类似问题的解决方案。在这个解决方案中,生产者和消费者一直运行,直到主任务通知他们停止为止。以这种方式,客户的数量是由时间控制的,而不是简单的循环计数。每一个生产商都会在客户进入的门口为他们命名,这样我们就可以看到这两个生产商都在工作。每个职员在处理客户时识别每个客户。“等候室”被实现为一个由十个元素组成的同步队列。synchronized队列处理所有数据同步问题,包括队列已满时挂起生产者,队列为空时挂起办事员(使用者)。
ada在包中定义程序模块。每个包必须有一个规范,它定义了模块的公共和私有接口。包通常还有一个定义模块行为的主体。主过程创建生产者任务类型和使用者任务类型的示例,这些示例立即开始运行。然后,main延迟(休眠)80秒,并为每个任务调用stop条目。

-----------------------------------------------------------------------
-- 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.
11dmarpk

11dmarpk2#

您所问问题的细节很复杂,但我可以帮助您理解java的监视器(wait、notify、notifyall)是如何用于生产者/消费者问题的。
在这里的中心是一排需要完成的工作。此队列(或其他有序数据结构)需要有两个同步方法:pop作业和push作业

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中不需要内部同步块,因为这两个方法都已经同步了

相关问题