Threads Java

/* From http://java.sun.com/docs/books/tutorial/index.html */
/*
 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * -Redistribution of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *
 * -Redistribution in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility.
 */
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class DiningPhilosophers extends javax.swing.JApplet implements
    ActionListener, ChangeListener {
  private JButton stopStartButton = new JButton("start");
  // delays can go from 0 to 10,000 milliseconds, initial value is 500
  int grabDelay = 500;
  private JSlider grabDelaySlider = new JSlider(JSlider.HORIZONTAL, 0, 100, 5);
  private JLabel label = new JLabel("  500 milliseconds");
  private JPanel philosopherArea;
  public ImageIcon[] imgs = new ImageIcon[3];
  Chopstick[] chopsticks = new Chopstick[NUMPHILS];
  String[] names = { "Arisduktle", "Dukrates", "Pythagoduke", "Duko",
      "Dukimedes" };
  static final int NUMPHILS = 5;
  static final int HUNGRYDUKE = 0;
  static final int RIGHTSPOONDUKE = 1;
  static final int BOTHSPOONSDUKE = 2;
  private int width = 0;
  private int height = 0;
  private double spacing;
  private static final double MARGIN = 10.0f;
  private Philosopher[] philosophers = new Philosopher[NUMPHILS];
  public void init() {
    imgs[HUNGRYDUKE] = new ImageIcon(getURL("images/hungryduke.gif"));
    imgs[RIGHTSPOONDUKE] = new ImageIcon(
        getURL("images/rightspoonduke.gif"));
    imgs[BOTHSPOONSDUKE] = new ImageIcon(
        getURL("images/bothspoonsduke.gif"));
    width = imgs[HUNGRYDUKE].getIconWidth() + (int) (MARGIN * 2.0);
    height = imgs[HUNGRYDUKE].getIconHeight() + (int) (MARGIN * 2.0);
    spacing = width + MARGIN;
    GridBagLayout gridBag = new GridBagLayout();
    GridBagConstraints c = new GridBagConstraints();
    JPanel contentPane = new JPanel();
    contentPane.setLayout(gridBag);
    philosopherArea = new JPanel(null);
    philosopherArea.setBackground(Color.white);
    Dimension preferredSize = createPhilosophersAndChopsticks();
    philosopherArea.setBorder(BorderFactory.createCompoundBorder(
        BorderFactory.createLoweredBevelBorder(), BorderFactory
            .createEmptyBorder(5, 5, 5, 5)));
    philosopherArea.setPreferredSize(preferredSize);
    c.fill = GridBagConstraints.BOTH;
    c.weighty = 1.0;
    c.gridwidth = GridBagConstraints.REMAINDER; //end row
    gridBag.setConstraints(philosopherArea, c);
    contentPane.add(philosopherArea);
    c.fill = GridBagConstraints.HORIZONTAL;
    c.weightx = 1.0;
    c.weighty = 0.0;
    gridBag.setConstraints(stopStartButton, c);
    contentPane.add(stopStartButton);
    c.gridwidth = GridBagConstraints.RELATIVE; //don't end row
    c.weightx = 1.0;
    c.weighty = 0.0;
    gridBag.setConstraints(grabDelaySlider, c);
    contentPane.add(grabDelaySlider);
    c.weightx = 0.0;
    c.gridwidth = GridBagConstraints.REMAINDER; //end row
    gridBag.setConstraints(label, c);
    contentPane.add(label);
    contentPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
    setContentPane(contentPane);
    stopStartButton.addActionListener(this);
    grabDelaySlider.addChangeListener(this);
  }
  public void actionPerformed(ActionEvent e) {
    if (stopStartButton.getText().equals("stop/reset")) {
      stopPhilosophers();
      stopStartButton.setText("start");
    } else if (stopStartButton.getText().equals("start")) {
      startPhilosophers();
      stopStartButton.setText("stop/reset");
    }
  }
  public void stateChanged(ChangeEvent e) {
    JSlider source = (JSlider) e.getSource();
    grabDelay = source.getValue() * 100;
    label.setText(String.valueOf(grabDelay + " milliseconds"));
  }
  public void startPhilosophers() {
    for (int i = 0; i < NUMPHILS; i++)
      philosophers[i].philThread.start();
  }
  public void stopPhilosophers() {
    for (int i = 0; i < NUMPHILS; i++)
      philosophers[i].philThread.interrupt();
  }
  public Dimension createPhilosophersAndChopsticks() {
    double x, y;
    double radius = 80.0;
    double centerAdj = 85.0;
    double radians;
    Dimension preferredSize = new Dimension(0, 0);
    /*
     * for a straight line y = MARGIN;
     */
    for (int i = 0; i < NUMPHILS; i++)
      chopsticks[i] = new Chopstick();
    for (int i = 0; i < NUMPHILS; i++) {
      /*
       * for a straight line x = i * spacing;
       */
      radians = i * (2.0 * Math.PI / (double) NUMPHILS);
      x = Math.sin(radians) * radius + centerAdj;
      y = Math.cos(radians) * radius + centerAdj;
      philosophers[i] = new Philosopher(this, i, imgs[HUNGRYDUKE]);
      philosophers[i].setBounds((int) x, (int) y, width, height);
      philosopherArea.add(philosophers[i]);
      if ((int) x > preferredSize.width)
        preferredSize.width = (int) x;
      if ((int) y > preferredSize.height)
        preferredSize.height = (int) y;
    }
    preferredSize.width += width;
    preferredSize.height += height;
    return preferredSize;
  }
  protected URL getURL(String filename) {
    URL codeBase = getCodeBase();
    URL url = null;
    try {
      url = new URL(codeBase, filename);
    } catch (java.net.MalformedURLException e) {
      System.out.println("Couldn't create image: "
          + "badly specified URL");
      return null;
    }
    return url;
  }
}
/*
 * This class requires no changes from the 1.0 version. It's kept here so the
 * rest of the example can compile.
 */
class Philosopher extends JLabel implements Runnable {
  private Chopstick leftStick, rightStick;
  private boolean sated;
  private DiningPhilosophers parent;
  private int position;
  Thread philThread = null;
  public Philosopher(DiningPhilosophers parent, int position, ImageIcon img) {
    super(parent.names[position], img, JLabel.CENTER);
    this.parent = parent;
    this.position = position;
    setVerticalTextPosition(JLabel.BOTTOM);
    setHorizontalTextPosition(JLabel.CENTER);
    // identify the chopsticks to my right and left
    this.rightStick = parent.chopsticks[position];
    if (position == 0) {
      this.leftStick = parent.chopsticks[parent.NUMPHILS - 1];
    } else {
      this.leftStick = parent.chopsticks[position - 1];
    }
    // I'm hungry
    this.sated = false;
    philThread = new Thread(this);
  }
  public void run() {
    try {
      while (true) {
        Thread.sleep((int) (Math.random() * parent.grabDelay));
        setText("     ");
        rightStick.grab();
        setIcon(parent.imgs[parent.RIGHTSPOONDUKE]);
        Thread.sleep((int) (Math.random() * parent.grabDelay));
        leftStick.grab();
        setIcon(parent.imgs[parent.BOTHSPOONSDUKE]);
        Thread.sleep((int) (Math.random() * parent.grabDelay));
        rightStick.release();
        leftStick.release();
        setIcon(parent.imgs[parent.HUNGRYDUKE]);
        setText("Mmmm!");
        sated = true;
        Thread.sleep((int) (Math.random() * parent.grabDelay * 4));
        sated = false;
      }
    } catch (java.lang.InterruptedException e) {
    }
    leftStick.releaseIfMine();
    rightStick.releaseIfMine();
    setIcon(parent.imgs[parent.HUNGRYDUKE]);
    setText(parent.names[position]);
    sated = false;
    philThread = new Thread(this);
  }
}
class Chopstick {
  Thread holder = null;
  public synchronized void grab() throws InterruptedException {
    while (holder != null)
      wait();
    holder = Thread.currentThread();
  }
  public synchronized void release() {
    holder = null;
    notify();
  }
  public synchronized void releaseIfMine() {
    if (holder == Thread.currentThread())
      holder = null;
    notify();
  }
}