Title: Thread Extra Examples
Course: CPS 261: Advanced Java Concepts ~ DL & MM MASTER

Thread Extra Examples

Dining Philosopher Problem

One of the classic problems that Academic people use to describe problems that you encounter in Operating Systems is the Dining Philosopher's problem. The image below describes the situation.

Philosophers think for a random amount of time. It doesn't require any special resources to think. Then a Philosopher needs to eat for a random amount of time. Unfortunately it takes 2 forks for a Philosopher to eat.

This means that Philosophers must have some algorithm to avoid starvation. For example, if each philosopher grabbed the fork to his left and then held onto it until he acquired the fork on his right, we would end up with a pile of dead starved philospher bodies.

If you run the solution below, you will see smiley faces for each philosopher who is eating. The code below has a table with 15 philosophers.

The algorithm used is to try to acquire the right fork. If this fails, wait for the right fork. If the right fork is acquired, we then try to acquire the left fork. If this succeeds, we go and eat. If it fails, it is very important that we put the right fork back on the table.

DiningPhilo.java


//Fork class with synchronized methods
class Fork{
    private int whichFork;
    private boolean available = true;

    Fork(int num)
    {
       whichFork = num;
    }
    public synchronized boolean get() {
        if (available)
        {
           available = false;
           return true;
        }
        return false;
    }
    public synchronized void put() {
        available = true;
        notifyAll();
    }
    public synchronized void waitForFork()
    {
       try
       {
           wait();
       }
       catch (InterruptedException e) { }
    }
}
// ******************************************************
// Class to represent the table containing the N Forks
class Table{
   // Array of Fork classes
   Fork[]  theForks;
   int maxForks;
   Table(int num)
   {
       maxForks = num;
       theForks = new Fork[num];
       for (int i=0; i < num; i++)
           theForks[i] = new Fork(i);
   }
   public void getForks(int rightFork){
       int leftFork = rightFork+1;
       if (leftFork >= maxForks) leftFork = 0;

       for ( ;;){ // We starve without forks.
           if (!theForks[rightFork].get() ) // Try to get the right fork
               {
                   // Failed to get fork --  Wait for right fork
                   theForks[rightFork].waitForFork();
                   continue;
               }
           if (!theForks[leftFork].get() ) // Try to get the left fork
               {
                   // Failed to get the left fork
                   theForks[rightFork].put(); // Return the right fork
                   // Wait for left fork
                   theForks[leftFork].waitForFork();
                   continue;
               }
           return;
       }


   }
   public void putForks(int rightFork){
       int leftFork = rightFork+1;
       if (leftFork >= maxForks) leftFork = 0;

       theForks[leftFork].put(); // Put down the left fork.
       theForks[rightFork].put(); // Put down the right fork.
   }
}
// ********************************************************
// Class that creates the displayable  output.  Eating philosophers
// show up with a smiley face, thinking philosophers are blank
class Display{
    private int number;
    boolean[] eating;
    Display(int n){
        number = n;
        eating = new boolean [n];
        for (int i=0; i < eating.length; i++)
            eating[i] = false;
    }
    synchronized void terminate(String msg)
    {
        System.out.println(msg);
    }
    synchronized void update(int philo, boolean eat_flag)
    {
        eating[philo] = eat_flag;
        System.out.println("------------------------------------------------------");
        for (int i=0; i < number; i++)
            if (i < 10)
                System.out.print(i + "    ");
             else
                System.out.print(i + "   ");
        System.out.println();
        for (int i=0; i < number; i++)
            if (eating[i])
                System.out.print(":-)  ");
            else
                System.out.print("     ");
        System.out.println();
    }
}

// ********************************************************
// Dining Philosopher Thread
class Philo extends Thread {
    private Table theTable;
    private Display theDisplay;
    private int number;
    private boolean keepRunning = true;

    public Philo(Display display, Table table, int number) {
        theTable = table;
        theDisplay = display;
        this.number = number;
    }
    public void stopRunning()
    {
       keepRunning = false;
       this.interrupt();
    }

    public void run() {
       int nSleep;

        for (; keepRunning; )
          {

           // Enter Thinking mode
            try {
                nSleep = (int)(Math.random()*3000);
                sleep(nSleep);
                } catch (InterruptedException e) { }
            if (!keepRunning)
               break;

            // Enter Eating mode
             try {
                   theTable.getForks(number);
                   if (keepRunning)
                   {
                        theDisplay.update(number, true); // we are now eating
                        nSleep = (int)(Math.random()*8000);
                        sleep(nSleep);
                    }
                 } catch (InterruptedException e) { }

             theDisplay.update(number, false); // we are about to start thinking
             theTable.putForks(number);
             if (!keepRunning)
               break;
           }// End of for loop for Philosopher

             theDisplay.terminate("Philosopher Exiting: "+number);

    }
}


//**********************************************************
 public class DiningPhilo {
    private final static int NUM_PHILOS = 15;
 public static void main(String[] args) {

    Table theTable= new Table(NUM_PHILOS);
    Philo[] thePhilos = new Philo[NUM_PHILOS];
    Display display = new Display(NUM_PHILOS);

       for (int i = 0; i < NUM_PHILOS; i++)
       {
           thePhilos[i] = new Philo(display, theTable, i);
           thePhilos[i].start();
       }

       try{ Thread.sleep(20000); }
       catch(InterruptedException e){}

       System.out.println("After 20 Seconds, we Terminate");
       for (int i=0; i < NUM_PHILOS; i++)
          thePhilos[i].stopRunning();

    }//End of main
}






A few points that didn't show up in the examples above:

Thread Thread.currentThread() - this static method is sometimes useful. It returns a pointer to the Thread that is currently executing.

boolean isInterrupted() - this method returns true if this thread has been interrupted.

One final point is that wait, notify and notifyAll must always be called in a synchronized section of code. They must also be called inside of method of the relavent object instance. Failing to follow these rules will result in an exception.

ColorBoxes.java

The first parameter contains the number of milliseconds to wait before changing colors. The second parameter contains the size of the square. Compile this and and run it with commands like:
java ColorBoxes 50 5
java ColorBoxes 1 5
java ColorBoxes 50 12

With no parameters it defaults to 50 20


import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;

class CBox extends JPanel implements Runnable {
    private Thread t;
    private int pause;
    private static final Color[] colors = {
        Color.black, Color.blue, Color.cyan,
        Color.darkGray, Color.gray, Color.green,
        Color.lightGray, Color.magenta,
        Color.orange, Color.pink, Color.red,
        Color.white, Color.yellow
    };
    private Color cColor = newColor();
    private static final Color newColor() {
        return colors[
                      (int)(Math.random() * colors.length)
                      ];
    }
    public void paint(Graphics  g) {
        g.setColor(cColor);
        Dimension s = getSize();
        g.fillRect(0, 0, s.width, s.height);
    }
    public CBox(int pause) {
        this.pause = pause;
        t = new Thread(this);
        t.start();
    }
    public void run() {
        while(true) {
            cColor = newColor();
            repaint();
            try {
                t.sleep(pause);
            } catch(InterruptedException e) {}
        }
    }
}

public class ColorBoxes extends JFrame {
    public ColorBoxes(int pause, int grid) {
        setTitle("ColorBoxes");
        setLayout(new GridLayout(grid, grid));
        for (int i = 0; i < grid * grid; i++)
            add(new CBox(pause));
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
    }
    public static void main(String[] args) {
        int pause = 50;
        int grid = 20;
        if(args.length > 0)
            pause = Integer.parseInt(args[0]);
        if(args.length > 1)
            grid = Integer.parseInt(args[1]);
        JFrame f = new ColorBoxes(pause, grid);
        f.setSize(500, 400);
        f.setVisible(true);
    }
}



Exercises

1. Create an application which writes to the screen what the user types. If the user doesn't type anything on 10 seconds, a message should show up on the screen saying:
Nothing received in the last 10 seconds

Hint: You need another thread in addition to the main thread. One of the threads will just read MyInput to collect user input. You will need a synchronizing class (can be done in many ways). The other thread will be waiting inside of a method in the synchronizing class (note that wait can have a timeout). This thread will wake up under either a timeout condition or because a message was sent to it to publish. The MyInput reading thread will call a method in the synchronizing class to set the message to be written.
2. Create a writer thread that contains synchronized methods postMessage, getMessage. The writer thread calls getMessage and writes the acquired messages to some logging file. Create several (5) reader threads that periodically send messages to the writer thread. Create a Mother thread that starts the whole thing going. The Mother thread creates the writer thread and the reader threads. The Mother thread will periodically check to see if the reader threads are alive and when they are all dead, it will tell the writer thread to terminate. In our first version of the writer task, we will only hang onto one String to be written. In an upgraded version, we will keep a linked list of Strings to be written. Discuss issues on why a circular queue might be desireable here.
3. Create a Copy program that uses 2 threads. One thread for reading the input file and a second thread for writing out the data. For a datastructure, create a circular queue using an array. Provide get and put methods for your circular queue. Each entry in the circular queue has a buffer and a number of bytes. Provide a synchronized get and put method to your circular queue.
4. Take the previous problem one step further. Create 3 threads. The first thread reads buffers into a circular que. The second thread pulls buffers out of the circular queue and upper cases the characters. The output is placed into a second circular queue. The third thread pulls buffers out of the second queue and writes out the data to a file. If you are looking for a lot text, copy from the Java Certification link.
Solution (don't peek until you have thought about this for a while)
5. Suppose you are writing software to process ATM transactions from multiple ATM threads. After each Transaction, the transaction needs to be written back to a single Audit file back at the Bank's head quarters. Simulate this job with a thread to write out the Audit File. Provide multiple ATM threads which send transaction messages to the Audit File Thread. Simulate an ATM thread by having each thread open up a "Canned" Transaction file which is read after random delays.
6. Create 5 threads that wait to be notified. The main thread takes commands like:
wakeup 3
wakeup 1

The main thread should wakeup the specified thread. Solve this by having an array of synchronization objects (one for each of the 5 threads). Each of the 5 threads can wait on its own unique object. The main thread can notify just the one specified by the command.
7. Solve the previous problem with one synchronization object. In this case, you will need to have all 5 threads waiting on the same object, and each thread will have to check if he is the "one" that should be scheduled.
8. Go back to the Collections class in the List-Collections link to discover how to create a synchronized ArrayList. Create 5 threads which put messages in this synchronized list at random times. After each thread has put out 5 messages, dump out the Synchronized ArrayList.
9. Rewrite the Dining Philosopher program to get rid of the Fork Class. Instead, make the Table class have synchronized functions for getForks and putForks. The forks can be managed as an array of available boolean flags. In this scheme, putting the forks down causes a notifyAll to occur on the Table object for all Philosophers needing forks.

Scheduling Games

The techniques in the following example are rarely used, but this example is designed to give you some idea about Thread scheduling.

1. This program implements a race. You can have a fair race or an unfair race (the default is unfair). In the unfair race we give each Racing Thread a different priority.
java Racing fair
or
java Racing unfair
The results of this program will be platform dependent, because we are in effect testing the scheduling characteristics of the Java Runtime system. When I ran this in class, the priorities (unfair race) seemed to have a much larger impact on Windows than on Linux.
2. We will create threads using both techniques described above. We will have a Racer class which is derived from Thread. Our main class is called Racing and it implements the Runnable interface. We will create 5 Threads based on the Racer class. We will also create one thread based on the Racing class. The Racing class will monitor the progress of each of the Racer threads an print out status updates indicating how much CPU time has been burned up by each Racer thread.
3. Normally, most threads encounter natural suspensions when they wait for input from I/O (i.e. keyboard, files, or for some event to occur).
4. In the following example, we are creating a Thread called Racer which burns up CPU time. The Racer thread will Hog as much CPU time. There is a yield_flag parameter passed to the Racer thread. If the yield_flag is set, then the Racer Thread will be a "good citizen" and volountarily give up the CPU after 500 iterations. This allows other Threads (probably another Racer Thread) to execute. Giving up the CPU is done by calling:
yield();
The yielding Thread can be rescheduled immediately if no one else wants the use the CPU. If other Threads want the CPU, then the yielding thread will sit in a Scheduler Queue waiting for another turn to use the CPU.
5. To turn on the yield flag, you need to have a second parameter that contains the value yield. The default is for the Racer to do no yielding. Here are some of your options:
java Racing fair yield
or
java Racing unfair noyield
or
java Racing fair noyield
or
java Racing unfair yield
6. To create an unfair race, we play games with Thread priorities. I would recommend that normally you don't mess with priorities. However, in this example we are exploring Thread Scheduling possibilities. When a thread is created, it is possible to change its priority with the Thread method:
setPriority(priority);
The priority possibilities is 1,2, ... 10. You can also use Thread.MIN_PRIORITY which is 1, Thread.MAX_PRIORITY which is 10, or Thread.NORM_PRIORITY which is 5.

Racing.java


class Racer extends Thread
{
    static long bigNumber = 999999999999999L;
    boolean yield_flag = false;
    long current_count = 0;
    Racer(boolean y){
        yield_flag = y;
    }
    public void run(){

        for (; current_count < bigNumber; current_count++)
        {
            double x;
            // if yield_flag is on, call yield every 500 iterations
            if (yield_flag && (current_count % 500) == 0)
                yield();
            x = Math.sqrt( Math.tan(13.9)); // burn up some time
        }
    }
}
class Racing implements Runnable
{
    int numRunners =5;
    Racer[] runners = new Racer[numRunners];

   // Just print out on the screen the amount of work done
   // by each Thread
    void screenUpdate()
    {
        long maxLoop=0;
	// Find the leader total.
        for(int i=0; i < numRunners; i++)
            if (runners[i].current_count > maxLoop)
                maxLoop = runners[i].current_count;
        System.out.println("------------------------------------");
        if (maxLoop == 0) return; // race not started yet

      // The leader always gets 40 stars
      // Other racers get an appropriate number of stars
      // to indicate how they compare with the leader.
        for (int i=0; i < numRunners; i++)
        {
            long curr = runners[i].current_count;
            long numStars = (curr * 40)/ maxLoop;
            for (long j=0; j < numStars; j++)
                System.out.print("*");
            System.out.println("  " + curr);
        }

    }
    public void run(){
        for (;;)
        {
            try {Thread.sleep(2000);}
            catch (InterruptedException e){}
            screenUpdate();
        }
    }

    void doit(boolean fair, boolean yield)
    {
        Thread display;

        System.out.println("fair: " + fair + "  yield: " + yield);

        // Create Threads with desired Priorities
        for(int i=0; i < numRunners; i++)
        {
           int priority = Thread.MIN_PRIORITY;
           if (!fair) priority += i;
           // Don't ever want to use a priority > MAX_PRIORITY
           priority = Math.min(Thread.MAX_PRIORITY, priority);
           runners[i] = new Racer(yield);
           runners[i].setPriority(priority);
        }
        // Use the fact that the Racing class implements
        // the Runnable Interface
        display = new Thread(this);
        display.setPriority(Thread.MAX_PRIORITY);
        display.start();

        // Start the Race
        for(int i=0; i < numRunners; i++)
           runners[i].start();
    }

    public static void main(String[] args)
    {
        boolean fair=false;
        boolean yield = false;
        Racing racing = new Racing();

        System.out.println("Useage: Racing [fair] [yield]");

        if (args.length > 0 && "fair".equals(args[0]))
            fair = true;
        if (args.length > 1 && "yield".equals(args[1]))
            yield = true;

        racing.doit(fair, yield);
        System.out.println("main is done");
     }

}


PizzaStore.java

So you decide to get into the Pizza business. This example doesn't really use any new concepts, but it provides a more meaningful example. To implement this we take the following steps:

1. We define a Pizza class for each order, and we create a PizzaStore class that contains a LinkedList of Pizza Orders.
2. We define a Customer Thread. A Customer thread is created for each new Pizza Order. The primary function of the Customer Thread is to wait for the Pizza order to complete.
3. We define a Baker Thread to bake the Pizzas. We create 3 Baker Threads to keep up with out business. Bakers remove Pizzas from the front of the LinkedList and bake the Pizzas.

Here is the sequence of activity:

1. The main routine of the PizzaStore allows us to type in Pizza Orders. For each Pizza Order, the orderPizza method is called. The orderPizza Method creates a new Pizza order and adds it to the tail of the LinkedList. The orderPizza method also calls notify to let any sleeping bakers know there is more work to do.
2. The main routine also creates a Customer Thread to wait for the Pizza to be baked. The primary job of the Customer Thread is to call the waitForPizza method in the Pizza class. This method will end up calling wait(), because it takes some time to bake a Pizza.
3. Meanwhile, if there is an available Baker, then the Pizza order has been removed from the LinkedList with a call to getPizzaToBake.
4. The Baker then calls the bake method in the Pizza class. This method takes some time to bake the Pizza. When the Pizza is done, the bake method calls notify. This call should wake up the hungry Customer.

To terminate this program, we need to do a Control-C which terminates everything.


import java.util.*;

class Pizza
{
    boolean done=false;
    String name;
    int bakerIdent=-1;
    String[] toppings;
    Pizza(String n, String[] t)
    {
        name =n;
        toppings =t;
    }
    void print()
    {
        System.out.println("Pizza for " + name + " BakerId = " + bakerIdent);
        System.out.print("toppings: ");
        for (int i=0; i < toppings.length; i++)
            System.out.print(toppings[i] + " ");
        System.out.println();
    }
    synchronized void bake(int id)
    {
        bakerIdent = id;
        try {
        // Takes 20 seconds to bake a Pizza
            Thread.sleep(20000);
            done = true;
            notify(); // Wake up Waiting Customer in waitForPizza
        }
        catch (InterruptedException e)
        {
        }
    }
    synchronized void waitForPizza()
    {
        while (!done )
        {
            try { wait(); } // Wait for Pizza Baking Completion
            catch (InterruptedException e) {}
        }
        System.out.println(" ******Customer: Pizza Delivered --- ready to eat");
        print();
    }
}
class Customer extends Thread
{
    Pizza pizza;
    Customer (Pizza p)
    {
        pizza =p;
    }
    public void run()
    {
        pizza.waitForPizza();
    }
}

class Baker extends Thread
{
    int ident;
    PizzaStore store;
    Baker (int id, PizzaStore ps)
    {
        ident = id;
        store = ps;
    }
    public void run()
    {
        Pizza pizza;

        for (;;)
        {
            pizza = store.getPizzaToBake();
            pizza.bake(ident);
        }
    }
}



class PizzaStore{
    LinkedList pizzaList = new LinkedList();

    synchronized Pizza orderPizza(String name, String[] toppings)
    {
        Pizza pizza= new Pizza(name, toppings);
        pizzaList.addLast(pizza);
        notify(); // Wake up any sleeping Bakers
        return pizza;
    }
    synchronized Pizza getPizzaToBake()
    {
        Pizza pizza=null;
        do
        {
            try
            {
                if (pizzaList.size() > 0)
                    pizza = (Pizza) pizzaList.removeFirst();
             }
             catch (NoSuchElementException e)
             {
                //We get here when the Bakers have
                //baked all of the Pizzas ordered so far
                pizza = null;
             }
            if (pizza == null)
            {
                try { wait();} // Wait for incomming Pizza Order
                catch (InterruptedException e){}
            }
        } while (pizza == null );
        return pizza;
    }

    public static void main(String[] args)
    {
        PizzaStore store = new PizzaStore();
        Baker[] baker = new Baker[3];
        Customer customer;
        Pizza pizza;
        Scanner in = new Scanner(System.in);
        String str, custName;
        String[] toppings;
        StringTokenizer token;
        int count;

        // Create 3 bakers
        for (int i=0; i < baker.length; i ++)
        {
          baker[i] = new Baker(i+1, store);
          baker[i].start();
        }
        for (;;)
        {
            System.out.println("ENTER: Name Topping1 Topping2 ...");
            System.out.println("ENTER: BYE to quit");
            str = in.nextLine();
            str = str.trim();
            if (str.equals("BYE")) break;
            token = new StringTokenizer(str);
            count = token.countTokens();
	    if (count <= 0)
	    {
	        System.out.println("incomplete order");
		continue;
	    }
            toppings = new String[ count -1];
            custName = (String)token.nextElement();
            for (int i=0; i < toppings.length; i++)
                toppings[i] = (String) token.nextElement();

            pizza = store.orderPizza(custName, toppings);
            customer = new Customer(pizza);
            customer.start();
        }


    }// End of Main
}


Terminating Processes:

If you look at online help for the Thread class, you will note some methods with the name of stop that are Deprecated. This means that the routine exists, but the recommendation is that you don't use it. In many cases it is a signal that future releases may not support these routines. Why do you think the stop routine is being removed???

The recommended way to remove threads is to get the thread to voluntarily terminate. In the following example, we will implement a Termination process that immediately gets rid of all of our threads using such a technique.

PizzaStore.java: Second Version

Suppose we want to have a command called BYE that lets us go out of business cleanly. If we just exit the main routine when we encounter the BYE command, we will leave 3 bakers continuing to run. We will also leave Customers waiting for their Pizzas. This is not a very professional way to go out of business. We would like to add some logic associated with BYE that will be as courteous as to deliver Pink slips to the Bakers. We also would like our Bakers to be polite enough to terminate their active Pizza Orders.

I have marked up the changes. Its a bit messy but seems to work. The only new feature that I have used is the method in the Thread class called
interrupt()
This method will wake a Thread from a sleep call or a wait call. The thread will get an InterruptedException in this case.


import java.util.*;

class Pizza
{
    boolean done=false;
    boolean inBusiness = true;
    String name;
    int bakerIdent=-1;
    String[] toppings;
    Pizza(String n, String[] t)
    {
        name =n;
        toppings =t;
    }
    void print()
    {
        System.out.println("Pizza for " + name + " BakerId = " + bakerIdent);
        System.out.print("toppings: ");
        for (int i=0; i < toppings.length; i++)
            System.out.print(toppings[i] + " ");
        System.out.println();
    }
    synchronized void bake(int id)
    {
        bakerIdent = id;
        try {
            Thread.sleep(20000);
            done = true;
            notify(); // Wake up Waiting Customer in waitForPizza
        }
        catch (InterruptedException e)
        {
            outOfBusiness();
        }
    }
    synchronized void outOfBusiness()
    {
       inBusiness = false;
       notify(); // let customer know we went out of business
       System.out.println("pizza.Out of business");
    }
    synchronized void waitForPizza()
    {
        while (!done && inBusiness)
        {
            try { wait(); }  // Wait for Pizza Baking Completion
            catch (InterruptedException e) {}
        }
        if (inBusiness)
            System.out.println(" ******Customer: Pizza Delivered --- ready to eat");
        else
            System.out.println(" ******Customer: Pizza Store out of business ");
        print();
    }
}
class Customer extends Thread
{
    Pizza pizza;
    Customer (Pizza p)
    {
        pizza =p;
    }
    public void run()
    {
        pizza.waitForPizza();
    }
}

class Baker extends Thread
{
    int ident;
    PizzaStore store;
    Baker (int id, PizzaStore ps)
    {
        ident = id;
        store = ps;
    }
    public void run()
    {
        Pizza pizza;

        for (;;)
        {
            pizza = store.getPizzaToBake();
            if (pizza == null) break; // Out of business
            pizza.bake(ident);
        }
        System.out.println("*****Baker " + ident + " terminating");
    }
}



class PizzaStore{
    LinkedList pizzaList = new LinkedList();
    boolean inBusiness = true;

    synchronized Pizza orderPizza(String name, String[] toppings)
    {
        Pizza pizza= new Pizza(name, toppings);
        pizzaList.addLast(pizza);
        notify(); // Wake up any sleeping Bakers
        return pizza;
    }
    synchronized Pizza getPizzaToBake()
    {
        Pizza pizza=null;
        if (!inBusiness) return null;
        do
        {
            try
            {
                if (pizzaList.size() > 0)
                    pizza = (Pizza) pizzaList.removeFirst();
             }
             catch (NoSuchElementException e)
             {
                pizza = null;
             }
            if (pizza == null)
            {
                try { wait();} // Wait for incomming Pizza Order
                catch (InterruptedException e){}
            }
        } while (pizza == null && inBusiness);
        return pizza;
    }
    synchronized void outOfBusiness()
    {
        inBusiness = false;
        notifyAll(); // Wake up all bakers so they can terminate
        while (pizzaList.size() > 0)
        {
	    // For all of the Pizzas sitting in the queue, we need to
	    // let hungry customers know that we are out of business
            Pizza pizza = (Pizza)pizzaList.removeFirst();
            pizza.outOfBusiness();
        }
    }

    public static void main(String[] args)
    {
        PizzaStore store = new PizzaStore();
        Baker[] baker = new Baker[3];
        Customer customer;
        Pizza pizza;
        Scanner in = new Scanner ( System.in);
        String str, custName;
        String[] toppings;
        StringTokenizer token;
        int count;

        // Create 3 bakers
        for (int i=0; i < baker.length; i ++)
        {
          baker[i] = new Baker(i+1, store);
          baker[i].start();
        }
        for (;;)
        {
            System.out.println("ENTER: Name Topping1 Topping2 ...");
            System.out.println("ENTER: BYE to quit");
            str = in.nextLine();
            str = str.trim();
            if (str.equals("BYE")) break;
            token = new StringTokenizer(str);
            count = token.countTokens();
	    if (count <= 0)
	    {
	        System.out.println("incomplete order");
		continue;
	    }
            toppings = new String[ count -1];
            custName = (String)token.nextElement();
            for (int i=0; i < toppings.length; i++)
                toppings[i] = (String) token.nextElement();

            pizza = store.orderPizza(custName, toppings);
            customer = new Customer(pizza);
            customer.start();
        }
        System.out.println("Exitting Loop Logic");
        store.outOfBusiness();
        for (int i=0; i < baker.length; i ++)
        {
          baker[i].interrupt();
        }

    }// End of main
}


Alternative approach to going out of business:

The above approach assumed that BYE meant I want to exit everything cleanly immediately. Create an alternative approach that isn't quite so impatient. Assume that it is OK for our bakers to empty the Pizza Queue and bake all outstanding orders before they exit their job. You should find this approach is a lot simpler --- but it takes a little longer to clean up.

Last Updated: July 22, 2014 10:23 PM