Runnable revisited | CodeGuru

Runnable revisited

Bruce Eckel’s Thinking in Java Contents | Prev | Next Earlier in this chapter, I suggested that you think carefully before making an applet or main Frame as an implementation of Runnable. If you take that approach, you can make only one of those threads in your program. This limits your flexibility if you decide […]

Written By
CodeGuru Staff
CodeGuru Staff
Mar 1, 2001
5 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

Earlier


in this chapter, I suggested that you think carefully before making an applet


or main


Frame

as an implementation of

Runnable.
If you take that approach, you can make only one of those threads in your
program. This limits your flexibility if you decide that you want to have more
than one thread of that type.

Of


course, if you must inherit from a class


and

you want to add threading behavior to the class,


Runnable

is the correct solution. The final example in this chapter exploits this by


making a


Runnable
Canvas

class that paints different colors on itself. This application is set up to


take values from the command line to determine how big the grid of colors is


and how long to


sleep( )

between color changes. By playing with these values you’ll discover some


interesting and possibly inexplicable features of threads:

//: ColorBoxes.java
// Using the Runnable interface
import java.awt.*;
import java.awt.event.*;
 
class CBox extends Canvas 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 Frame {
  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 = 8;
    if(args.length > 0)
      pause = Integer.parseInt(args[0]);
    if(args.length > 1)
      grid = Integer.parseInt(args[1]);
    Frame f = new ColorBoxes(pause, grid);
    f.setSize(500, 400);
    f.setVisible(true);
  }
} ///:~ 
ColorBoxes

is a typical application with a constructor that sets up the GUI. This


constructor takes an argument of


int
grid

to set up the

GridLayout
so that it has
grid
cells in each dimension. Then it adds the appropriate number of
CBox
objects to fill the grid, passing the
pause
value to each one. In
main( )
you can see how
pause
and
grid
have default values that can be changed if you pass in command-line arguments.
CBox

is


where all the work takes place. This is inherited from

Canvas
and it implements the
Runnable
interface so each
Canvas
can also be a
Thread.
Remember that when you implement
Runnable,
you don’t make a
Thread
object, just a class that has a
run( )
method. Thus, you must explicitly create a
Thread
object and hand the
Runnable
object to the constructor, then call
start( )
(this happens in the constructor). In
CBox
this thread is called
t.

Notice


the array


colors

,


which is an enumeration of all the colors in class


Color

.


This is used in


newColor( )

to produce a randomly-selected color. The current cell color is


cColor

.

paint( )

is quite simple – it just sets the color to


cColor

and fills the entire canvas with that color.

In


run( )

,


you see the infinite loop that sets the


cColor

to a new random color and then calls


repaint( )

to show it. Then the thread goes to


sleep( )

for the amount of time specified on the command line.

Precisely


because this design is flexible and threading is tied to each


Canvas

element, you can experiment by making as many threads as you want. (In reality,


there is a restriction imposed by the number of threads your JVM can


comfortably handle.)

This


program also makes an interesting benchmark, since it can show dramatic speed


differences between one JVM implementation and another.


Too
many threads

At


some point, you’ll find that


ColorBoxes

bogs down. On my machine, this occurred somewhere after a 10 x 10 grid. Why


does this happen? You’re naturally suspicious that the AWT might have


something to do with it, so here’s an example that tests that premise by


making fewer threads. The code is reorganized so that a


Vector
implements
Runnable

and that


Vector

holds a number of color blocks and randomly chooses ones to update. Then a


number of these


Vector

objects are created, depending roughly on the grid dimension you choose. As a


result, you have far fewer threads than color blocks, so if there’s a


speedup we’ll know it was because there were too many threads in the


previous example:

//: ColorBoxes2.java
// Balancing thread use
import java.awt.*;
import java.awt.event.*;
import java.util.*;
 
class CBox2 extends Canvas {
  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)
    ];
  }
  void nextColor() {
    cColor = newColor();
    repaint();
  }
  public void paint(Graphics  g) {
    g.setColor(cColor);
    Dimension s = getSize();
    g.fillRect(0, 0, s.width, s.height);
  }
}
 
class CBoxVector
  extends Vector implements Runnable {
  private Thread t;
  private int pause;
  public CBoxVector(int pause) {
    this.pause = pause;
    t = new Thread(this);
  }
  public void go() { t.start(); }
  public void run() {
    while(true) {
      int i = (int)(Math.random() * size());
      ((CBox2)elementAt(i)).nextColor();
      try {
        t.sleep(pause);
      } catch(InterruptedException e) {}
    }
  }
}
 
public class ColorBoxes2 extends Frame {
  private CBoxVector[] v;
  public ColorBoxes2(int pause, int grid) {
    setTitle("ColorBoxes2");
    setLayout(new GridLayout(grid, grid));
    v = new CBoxVector[grid];
    for(int i = 0; i < grid; i++)
      v[i] = new CBoxVector(pause);
    for (int i = 0; i < grid * grid; i++) {
      v[i % grid].addElement(new CBox2());
      add((CBox2)v[i % grid].lastElement());
    }
    for(int i = 0; i < grid; i++)
      v[i].go();
    addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
  }
  public static void main(String[] args) {
    // Shorter default pause than ColorBoxes:
    int pause = 5;
    int grid = 8;
    if(args.length > 0)
      pause = Integer.parseInt(args[0]);
    if(args.length > 1)
      grid = Integer.parseInt(args[1]);
    Frame f = new ColorBoxes2(pause, grid);
    f.setSize(500, 400);
    f.setVisible(true);
  }
} ///:~ 

In


ColorBoxes2

an array of


CBoxVector

is created and initialized to hold


grid
CBoxVector

s,


each of which knows how long to sleep. An equal number of


Cbox2

objects is then added to each


CBoxVector

,


and each vector is told to


go( )

,


which starts its thread.

CBox2

is similar to


CBox

:


it paints itself with a randomly-chosen color. But that’s


all

a


CBox2

does. All of the threading has been moved into


CBoxVector

.

The


CBoxVector

could also have inherited


Thread

and had a member object of type


Vector

.


That design has the advantage that the


addElement( )

and


elementAt( )

methods could then be given specific argument and return value types instead of


generic


Object

s.


(Their names could also be changed to something shorter.) However, the design


used here seemed at first glance to require less code. In addition, it


automatically retains all the other behaviors of a


Vector

.


With all the casting and parentheses necessary for


elementAt( )

,


this might not be the case as your body of code grows.

As


before, when you implement


Runnable

you don’t get all of the equipment that comes with


Thread

,


so you have to create a new


Thread

and hand yourself to its constructor in order to have something to


start( )

,


as you can see in the


CBoxVector

constructor and in


go( )

.


The


run( )

method simply chooses a random element number within the vector and calls


nextColor( )

for that element to cause it to choose a new randomly-selected color.

Upon


running this program, you see that it does indeed run faster and respond more


quickly (for instance, when you interrupt it, it stops more quickly), and it


doesn’t seem to bog down as much at higher grid sizes. Thus, a new factor


is added into the threading equation: you must watch to see that you


don’t have “too many threads” (whatever that turns out to


mean for your particular program and platform). If you do, you must try to use


techniques like the one above to “balance” the number of threads in


your program. If you see performance problems in a multithreaded program you


now have a number of issues to examine:

  1. Do
    you have enough calls to
    sleep( ),
    yield( ),
    and/or
    wait( )?
  2. Are
    calls to
    sleep( )
    long enough?
  3. Are
    you running too many threads?
  4. Have
    you tried different platforms and JVMs?

Issues


like this are one reason that multithreaded programming is often considered an


art.


Contents

|

Prev

|

Next
CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.