Threads2 (other files are same as before)

Main.java  [download]

import java.awt.*;
import javax.swing.*;
import java.util.concurrent.*;

/**
 * Main program for Threads demo
 */
public class Main extends JFrame {
    /** Our Row's */
    private CopyOnWriteArrayList<Row> rows = new CopyOnWriteArrayList<Row> ();

    public static void main (String [] args) {
	java.awt.EventQueue.invokeLater (new Runnable() {
            public void run() {
		new Main ();
            }
        });
    }

    public Main () {
	// Window setup
	setSize (500, 500);
	Container content = getContentPane();
	setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);

	// Our canvas
	MyCanvas canvas = new MyCanvas (this);
	content.add (canvas);

	// Show our window
	setVisible (true);

	// First create our Supply row
	SupplyRow supply = new SupplyRow (new Rectangle (10, 10, 500-40, 40), //*1 One supply row
					  canvas); //*1
	rows.add (supply); //*1

	// Now create our regular ConsumerRow's and start() them
	for (int i=0; i<5; i++) {
		Row r = new ConsumerRow (new Rectangle (10, 100+i*75, 500-40, 40), //*2 5 Consumer rows
					 canvas, supply); //*2
		rows.add (r); //*2
	}
    }

    public void drawCanvas (Graphics g) {
	// Draw each of our Row's
	for (Row r: rows) r.draw (g); //*3 Draw all of our Row's (polymorphism)
    }
}

Row.java  [download]

import java.awt.*;
import java.util.concurrent.*;

/**
 * This is now just an abstract base class
 * for SupplyRow and ConsumerRow
 */
abstract public class Row { //*1 Abstract base class, mainly for drawing
    protected MyCanvas canvas;
    protected Rectangle loc;
    protected CopyOnWriteArrayList<Box> boxes = new CopyOnWriteArrayList<Box> (); //*1

    public Row (Rectangle loc, MyCanvas canvas) {
	this.loc = loc;
	this.canvas = canvas;
    }

    public void draw (Graphics g) { //*1
	Graphics2D g2 = (Graphics2D) g;

	g2.draw (loc); //*1

	// We tell each Box where to draw itself
	int boxsize = loc.height-2*5;
	Rectangle boxloc = new Rectangle (loc.x+10, loc.y+5,
					  boxsize, boxsize);

	// Draw each of our Box's
	for (Box b: boxes) { //*1
	    b.draw (g, boxloc); //*1
	    boxloc.x = boxloc.x + boxsize + 10;
	}
    }
}

ConsumerRow.java  [download]

import java.awt.*;

/**
 * This is like the old plain Row
 */
public class ConsumerRow extends Row implements Runnable { //*1 Uses a thread, like previous Row class
    private SupplyRow supply;

    public ConsumerRow (Rectangle loc, MyCanvas canvas, SupplyRow supply) {
	super (loc, canvas);
	this.supply = supply;

	// Start a new thread to run us
	Thread thread = new Thread (this);
	thread.start();
    }

    /**
     * Method for Runnable
     * Thread quits when it can't get another box
     */
    public void run () {
	while (true) {
	    // Try and get another box
	    Box b = supply.take (); //*2 Call special synchronized method to try to get a box

	    // If no more boxes, quit loop (and hence thread)
	    if (b==null) break; //*3 If no boxes, quit loop (and hence thread)

	    boxes.add (b);
	    // Since we just added to our list, need repaint
	    canvas.repaint ();

	    try {
		Thread.sleep (2000); // 2 seconds
	    } catch (InterruptedException e) { }
	}
	System.out.println ("Row at y = " + loc.y + " finished");
    }
}

SupplyRow.java  [download]

import java.awt.*;

/**
 * SupplyRow starts with fixed list of Box's and gives them away,
 * is not a process
 */
public class SupplyRow extends Row { //*1 Passive object, NOT a thread
    public SupplyRow (Rectangle loc, MyCanvas canvas) {
	super (loc, canvas);

	// Our initial set of boxes
	for (int i=0; i<22; i++) {
	    boxes.add (new Box ());
	}
    }

    /**
     * Give away one of our boxes
     * Need a single, "synchronized" routine
     * that reports whether we have any to give
     * AND takes it ("test and set")
     * So this routine tries to give a box,
     * returns the Box if succeeds, or null if fails
     */
    public synchronized Box take() { //*2 Need synchronized method
	if (boxes.size()>0) { //*3 Tests and takes in one uninterruptible unit
	    Box ans = boxes.remove (0); //*3
	    canvas.repaint (); //*4 Since we just decreased our list, need repaint
	    return ans; //*3
	}
	else return null; //*3
    } //*2
}