Threads2 (other files are same as Threads1)

File: java/Threads2/Main.java

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

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

    public static void main (String [] args) {
	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),
					  canvas);
	rows.add (supply);

	// 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),
				     canvas, supply);
	    rows.add (r);
	}
    }

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

File: java/Threads2/Row.java

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

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

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

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

	g2.draw (loc);

	// 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) {
	    b.draw (g, boxloc);
	    boxloc.x = boxloc.x + boxsize + 10;
	}
    }
}

File: java/Threads2/ConsumerRow.java

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

/**
 * This is like the old plain Row
 */
public class ConsumerRow extends Row implements Runnable {
    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 ();

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

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

	    try {
		Thread.currentThread().sleep (1000); // 1 second
	    } catch (InterruptedException e) { }
	}
	System.out.println ("Row at y = " + loc.y + " finished");
    }
}

File: java/Threads2/SupplyRow.java

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

/**
 * SupplyRow starts with fixed list of Box's and gives them away,
 * is not a process
 */
public class SupplyRow extends Row {
    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() {
	if (boxes.size()>0) {
	    Box ans = (Box) boxes.remove (0);
	    // Since we just decreased our list, need repaint
	    canvas.repaint ();
	    return ans;
	}
	else return null;
    }
}