Chapter 19: Threads

The exercises here are taken from my forthcoming book The Java Developer's Resource.

Quiz

  1. What's the difference between a thread created as a subclass of java.lang.Thread and one created as an implementation of Runnable?

    As threads, both behave the same. However, a thread that implements runnable can also extend another class.

  2. Why should synchronized blocks of code call wait()?

    To give other waiting threads time to execute and prevent deadlock. Thread.yield() also works for this purpose.

Exercises

  1. Add a Color property to the balls.

    import java.awt.Graphics;
    import java.applet.Applet;
    import java.awt.Rectangle;
    import java.awt.Color;
    import java.awt.Event;
    import java.awt.Dimension;
    
    public class TwoBall extends Applet implements Runnable {
    
      Ball b1, b2;
      boolean bouncing;
      Thread t;
      
      public void init () {
      
        b1 = new Ball(10, 32, size(), Color.red);
        b1.start();
        b2 = new Ball(155, 75, size(), Color.yellow);
        b2.start();
        bouncing = true;
        t = new Thread(this);
        t.start();
         
      }
      
      
      public void paint (Graphics g) {
        g.setColor(b1.getColor());
        g.fillOval(b1.getX(), b1.getY(), b1.getWidth(), b1.getHeight());
        g.setColor(b2.getColor());
        g.fillOval(b2.getX(), b2.getY(), b2.getWidth(), b2.getHeight());
      }
      
      public boolean mouseUp(Event e, int x, int y) {
        if (bouncing) {
          b1.stop();
          b2.stop();
          t.stop();
          bouncing = false;
        }
        else {
          b1.start();
          b2.start();
          t.start();
          bouncing = true;
        }
        return true;
      }
    
      public void run() {
    
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
    
        while (true) {  // infinite loop
          repaint();
          try {
            Thread.currentThread().sleep(10);
          }
          catch (Exception e) {
          
          }
        }
        
      }
    
    
    }
    
    import java.awt.Rectangle;
    import java.awt.Color;
    import java.awt.Dimension;
    
    
    class Ball extends Thread {
    
      private Rectangle r;
      private int x_increment = 1;
      private int y_increment = 1;
      private Dimension bounds;
      Color theColor;
    
      public Ball(int x, int y, Dimension d) {
        r = new Rectangle(x, y, 20, 20);
        bounds = d;
        theColor = Color.black;
      }
      
      public Ball(int x, int y, Dimension d, Color c) {
        r = new Rectangle(x, y, 20, 20);
        bounds = d;
        theColor = c;
      }
    
    
      public int getX() {
        return r.x;
      }
    
      public int getY() {
        return r.y;
      }
    
      public int getHeight() {
        return r.height;
      }
    
      public int getWidth() {
        return r.width;
      }
      
      public Color getColor()  {
        return theColor;
      }
      
      public void setColor(Color c) {
        theColor = c;
      }
    
      public void run() {
    
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
    
        while (true) {  // infinite loop
          r.x += x_increment;
          r.y += y_increment;
          if (r.x + r.width >= bounds.width || r.x < 0) x_increment *= -1;
          if (r.y + r.height >= bounds.height || r.y < 0) y_increment *= -1;
          try {
            Thread.currentThread().sleep(10);
          }
          catch (Exception e) {
          
          }
        }
        
      }
    
    }
    
  2. The balls in Program 19.10 always move in forty-five degree angles. Allow a broader behavior of the balls.

    The trick to this problem is to use doubles to represent the position and and increment of the balls. This requires substantial changes to the internal structure of the Ball class, but the external interface remains unchanged except for the appearance of a couple of new constructors.

    import java.awt.Rectangle;
    import java.awt.Color;
    import java.awt.Dimension;
    
    
    public class Ball extends Thread {
    
      private double x, y, width, height;
      private double x_increment;
      private double y_increment;
      private Dimension bounds;
      Color theColor;
    
    
    
      public Ball(int x, int y, Dimension d) {
      
        this(x, y, d, Color.black, 20.0, 20.0, 1.0, 1.0);
    
      }
        
      public Ball(int x, int y, Dimension d, Color c) {
    
        this(x, y, d, c, 20.0, 20.0, 1.0, 1.0);
        
      }
    
      
      public Ball(int x, int y, Dimension d, Color c, double x_increment, double y_increment) {
    
        this(x, y, d, c, 20.0, 20.0, x_increment, y_increment);
        
      }
    
      
      public Ball(int x, int y, Dimension d, double x_increment, double y_increment) {
    
        this(x, y, d, Color.black, 20.0, 20.0, x_increment, y_increment);
        
      }
      
      
      public Ball(double x, double y, Dimension d, Color c, double width, double height, double x_increment, double y_increment) {
      
        this.x = x;
        this.y = y;
        this.x_increment = x_increment;
        this.y_increment = y_increment;
        this.width = width;
        this.height = height;
        bounds = d;
        theColor = c;
    
      }
    
    
      public int getX() {
        return (int) x;
      }
    
      public int getY() {
        return (int) y;
      }
    
      public int getHeight() {
        return (int) height;
      }
    
      public int getWidth() {
        return (int) width;
      }
      
      public Color getColor()  {
        return theColor;
      }
      
      public void setColor(Color c) {
        theColor = c;
      }
    
      public void run() {
    
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
    
        while (true) {  // infinite loop
          x += x_increment;
          y += y_increment;
          if (x + width >= bounds.width || x < 0) x_increment *= -1;
          if (y + height >= bounds.height || y < 0) y_increment *= -1;
          try {
            Thread.currentThread().sleep(10);
          }
          catch (Exception e) {
          
          }
        }
        
      }
    
    }
  3. Write a multiball applet where the number of balls is a PARAM that is defined in HTML. You'll probably need to store the balls in a Vector. You should also allow the size, color, starting positions and directions of each ball to be given in a PARAM.

    import java.awt.Graphics;
    import java.applet.Applet;
    import java.awt.Rectangle;
    import java.awt.Color;
    import java.awt.Event;
    import java.awt.Dimension;
    import java.util.StringTokenizer;
    
    public class MultiBall extends Applet implements Runnable {
    
      Ball[] balls;
      boolean bouncing;
      Thread t;
      
      public void init () {
      
        int numballs;
        try {
          numballs = Integer.parseInt(getParameter("numballs"));
        }
        catch (Exception e) {
          numballs = 0;    
        }
        
        balls = new Ball[numballs];
        
        // Read Ball parameters in format
        // 
        for (int i = 0; i < numballs; i++) {
          String s = getParameter("Ball" + i);
          // missing parameter
          if (s == null) continue;
          StringTokenizer st=new StringTokenizer(s);
          if (st.countTokens() < 5) continue;
          double x = Double.valueOf(st.nextToken()).doubleValue();
          double y = Double.valueOf(st.nextToken()).doubleValue();
          double x_increment = Double.valueOf(st.nextToken()).doubleValue();
          double y_increment = Double.valueOf(st.nextToken()).doubleValue();
          String color = st.nextToken();
          Color c;
          if (color.equalsIgnoreCase("red")) {
            c = Color.red;
          }
          else if (color.equalsIgnoreCase("yellow")) {
            c = Color.yellow;
          }
          else if (color.equalsIgnoreCase("blue")) {
            c = Color.blue;
          }
          else if (color.equalsIgnoreCase("white")) {
            c = Color.white;
          }
          else if (color.equalsIgnoreCase("green")) {
            c = Color.green;
          }
          else {
            c = Color.black;
          }
          balls[i] = new Ball(x, y, size(), c, 20.0, 20.0, x_increment, y_increment);
          balls[i].start();
        }
        bouncing = true;
        t = new Thread(this);
        t.start();
         
      }
      
      
      public void paint (Graphics g) {
        for (int i = 0; i < balls.length; i++) {
          g.setColor(balls[i].getColor());
          g.fillOval(balls[i].getX(), balls[i].getY(), balls[i].getWidth(), balls[i].getHeight());
        }
      }
      
      public boolean mouseUp(Event e, int x, int y) {
        if (bouncing) {
          for (int i = 0; i < balls.length; i++) {
            balls[i].stop();
          }
          t.stop();
          bouncing = false;
        }
        else {
          for (int i = 0; i < balls.length; i++) {
            balls[i].start();
          }
          t.start();
          bouncing = true;
        }
        return true;
      }
    
      public void run() {
    
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
    
        while (true) {  // infinite loop
          repaint();
          try {
            Thread.currentThread().sleep(10);
          }
          catch (Exception e) {
          
          }
        }
        
      }
    
    }
    
  4. Hard: Allow the balls to bounce off each other as well as the walls.


[ Exercises | Cafe Au Lait | Books | Trade Shows | Links | FAQ | Tutorial | User Groups ]

Copyright 1996 Elliotte Rusty Harold
elharo@sunsite.unc.edu
Last Modified September 3, 1996