0

I have a thread subclass which represents arrivals at a public office and a thread subclass which represents the caseworkers.

Every time a person is arrived, the person's name is entered. There are 2 entrances for the office where the arrivals can enter their name. There can be no more than 10 persons in the waiting room. If there are more, they must wait until a place is available.

The caseworkers call the names that have been entered. There are 2 caseworkers. Of course, the names can only be called up if they have been entered and they is called in the order in which they have been entered (like a queue). When all the names are called up, the caseworkers must wait until more people arrive.

The threads for both the arrivals and the caseworkers sleep in a random number of seconds (from 1-10 seconds) every time.

I also have a common-class containing an arraylist with a list of names as a method and some methods for calling a name and entering a name. It also contains the append and take methods which is used to solve the problem (java's monitor).

Here is my code so far:

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

}

The problem is that some names are called before they are entered and I also get an ArrayOutOfBounceException even though there are 20 names in the list and there is a loop that retrieves the 20 names from the list in the 2 thread-subclasses.

Any help would be appreciated!

user3740970
  • 389
  • 1
  • 3
  • 16
  • Just a note, if you're not doing this for practice reasons only, you can use a`BlockingQueue` which will do the synchronizing for you. – daniu Dec 06 '18 at 16:14

2 Answers2

1

The details of the question you have asked are complicated, but I can help you understand how java's monitors (wait, notify, notifyAll) are used in the producer / consumer problem.

At the center of this a queue of jobs that need to get done. This queue (or other ordered data structure) needs to have two synchronized methods: pop a job, and push a job

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 threads call pop to get the next job. If there's no next job, the thread waits to be woken when a Producer thread pushes another job onto the queue and notify is called. Note that in the example I've given you don't need internal synchronized blocks in pop and push, since both methods are already synchronized

ControlAltDel
  • 33,923
  • 10
  • 53
  • 80
0

Here is a solution to a very similar problem programmed using Ada. In this solution the producers and consumers run until they are signaled to stop by the main task. In this manner the number of customers is controlled by time and not a simple loop count. Each producer names the customers by the door that they enter so that we can see that both producers actually work. Each clerk identifies each customer as the customer is handled. The "waiting room" is implemented as a synchronized queue of ten elements. The synchronized queue handles all the data synchronization issues including suspending producers when the queue is full and suspending clerks (consumers) when the queue is empty.

Ada defines program modules in packages. Each package must have a specification, which defines the public and private interfaces to the module. Packages normally also have a body which defines the behavior of the module. The main procedure creates instances of the producer task type and the consumer task type which begin running immediately. The main then delays (sleeps) for 80 seconds and calls the Stop entry for each task.

-----------------------------------------------------------------------
-- 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;

The package body is:

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;

The main procedure for this program is:

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;

Finally the output of one execution of this program is:

                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.
Jim Rogers
  • 4,822
  • 1
  • 11
  • 24