What Are Threads?


Let us start by looking at a program that does not use multiple threads and that, as a consequence, makes it difficult for the user to perform several tasks with that program. After we dissect it, we then show you how easy it is to have this program run separate threads. This program animates a bouncing ball by continually moving the ball, finding out if it bounces against a wall, and then redrawing it. (See Figure 1-1.)

Figure 1-1. Using threads to animate bouncing balls


As soon as you click the Start button, the program launches a ball from the upper-left corner of the screen and the ball begins bouncing. The handler of the Start button calls the addBall method. That method contains a loop running through 1,000 moves. Each call to move moves the ball by a small amount, adjusts the direction if it bounces against a wall, and then redraws the panel.

 Ball ball = new Ball(); panel.add(ball); for (int i = 1; i <= STEPS; i++) {    ball.move(panel.getBounds());    panel.paint(panel.getGraphics());    Thread.sleep(DELAY); } 

The static sleep method of the Thread class pauses for the given number of milliseconds.

The call to Thread.sleep does not create a new threadsleep is a static method of the THRead class that temporarily stops the activity of the current thread.

The sleep method can throw an InterruptedException. We discuss this exception and its proper handling later. For now, we simply terminate the bouncing if this exception occurs.

If you run the program, the ball bounces around nicely, but it completely takes over the application. If you become tired of the bouncing ball before it has finished its 1,000 moves and click the Close button, the ball continues bouncing anyway. You cannot interact with the program until the ball has finished bouncing.

NOTE

If you carefully look over the code at the end of this section, you will notice the call

 panel.paint(panel.getGraphics()) 

inside the move method of the Ball class. That is pretty strangenormally, you'd call repaint and let the AWT worry about getting the graphics context and doing the painting. But if you try to call panel.repaint() in this program, you'll find that the panel is never repainted because the addBall method has completely taken over all processing. In the next program, in which we use a separate thread to compute the ball position, we'll again use the familiar repaint.


Obviously, the behavior of this program is rather poor. You would not want the programs that you use behaving in this way when you ask them to do a time-consuming job. After all, when you are reading data over a network connection, it is all too common to be stuck in a task that you would really like to interrupt. For example, suppose you download a large image and decide, after seeing a piece of it, that you do not need or want to see the rest; you certainly would like to be able to click a Stop or Back button to interrupt the loading process. In the next section, we show you how to keep the user in control by running crucial parts of the code in a separate thread.

Example 1-1 shows the code for the program.

Example 1-1. Bounce.java
   1. import java.awt.*;   2. import java.awt.event.*;   3. import java.awt.geom.*;   4. import java.util.*;   5. import javax.swing.*;   6.   7. /**   8.    Shows an animated bouncing ball.   9. */  10. public class Bounce  11. {  12.    public static void main(String[] args)  13.    {  14.       JFrame frame = new BounceFrame();  15.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  16.       frame.setVisible(true);  17.    }  18. }  19.  20. /**  21.    A ball that moves and bounces off the edges of a  22.    rectangle  23. */  24. class Ball  25. {  26.    /**  27.       Moves the ball to the next position, reversing direction  28.       if it hits one of the edges  29.    */  30.    public void move(Rectangle2D bounds)  31.    {  32.       x += dx;  33.       y += dy;  34.       if (x < bounds.getMinX())  35.       {  36.          x = bounds.getMinX();  37.          dx = -dx;  38.       }  39.       if (x + XSIZE >= bounds.getMaxX())  40.       {  41.          x = bounds.getMaxX() - XSIZE;  42.          dx = -dx;  43.       }  44.       if (y < bounds.getMinY())  45.       {  46.          y = bounds.getMinY();  47.          dy = -dy;  48.       }  49.       if (y + YSIZE >= bounds.getMaxY())  50.       {  51.          y = bounds.getMaxY() - YSIZE;  52.          dy = -dy;  53.       }  54.    }  55.  56.    /**  57.       Gets the shape of the ball at its current position.  58.    */  59.    public Ellipse2D getShape()  60.    {  61.       return new Ellipse2D.Double(x, y, XSIZE, YSIZE);  62.    }  63.  64.    private static final int XSIZE = 15;  65.    private static final int YSIZE = 15;  66.    private double x = 0;  67.    private double y = 0;  68.    private double dx = 1;  69.    private double dy = 1;  70. }  71.  72. /**  73.    The panel that draws the balls.  74. */  75. class BallPanel extends JPanel  76. {  77.    /**  78.       Add a ball to the panel.  79.       @param b the ball to add  80.    */  81.    public void add(Ball b)  82.    {  83.       balls.add(b);  84.    }  85.  86.    public void paintComponent(Graphics g)  87.    {  88.       super.paintComponent(g);  89.       Graphics2D g2 = (Graphics2D) g;  90.       for (Ball b : balls)  91.       {  92.          g2.fill(b.getShape());  93.       }  94.    }  95.  96.    private ArrayList<Ball> balls = new ArrayList<Ball>();  97. }  98.  99. /** 100.    The frame with panel and buttons. 101. */ 102. class BounceFrame extends JFrame 103. { 104.    /** 105.       Constructs the frame with the panel for showing the 106.       bouncing ball and Start and Close buttons 107.    */ 108.    public BounceFrame() 109.    { 110.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 111.       setTitle("Bounce"); 112. 113.       panel = new BallPanel(); 114.       add(panel, BorderLayout.CENTER); 115.       JPanel buttonPanel = new JPanel(); 116.       addButton(buttonPanel, "Start", 117.          new ActionListener() 118.          { 119.             public void actionPerformed(ActionEvent event) 120.             { 121.                addBall(); 122.             } 123.          }); 124. 125.       addButton(buttonPanel, "Close", 126.          new ActionListener() 127.          { 128.             public void actionPerformed(ActionEvent event) 129.             { 130.                System.exit(0); 131.             } 132.          }); 133.       add(buttonPanel, BorderLayout.SOUTH); 134.    } 135. 136.    /** 137.       Adds a button to a container. 138.       @param c the container 139.       @param title the button title 140.       @param listener the action listener for the button 141.    */ 142.    public void addButton(Container c, String title, ActionListener listener) 143.    { 144.       JButton button = new JButton(title); 145.       c.add(button); 146.       button.addActionListener(listener); 147.    } 148. 149.    /** 150.       Adds a bouncing ball to the panel and makes 151.       it bounce 1,000 times. 152.    */ 153.    public void addBall() 154.    { 155.       try 156.       { 157.          Ball ball = new Ball(); 158.          panel.add(ball); 159. 160.          for (int i = 1; i <= STEPS; i++) 161.          { 162.             ball.move(panel.getBounds()); 163.             panel.paint(panel.getGraphics()); 164.             Thread.sleep(DELAY); 165.          } 166.       } 167.       catch (InterruptedException e) 168.       { 169.       } 170.    } 171. 172.    private BallPanel panel; 173.    public static final int DEFAULT_WIDTH = 450; 174.    public static final int DEFAULT_HEIGHT = 350; 175.    public static final int STEPS = 1000; 176.    public static final int DELAY = 3; 177. } 


 java.lang.Thread 1.0 

  • static void sleep(long millis)

    sleeps for the given number of milliseconds.

    Parameters:

    millis

    The number of milliseconds to sleep


Using Threads to Give Other Tasks a Chance

We will make our bouncing-ball program more responsive by running the code that moves the ball in a separate thread. In fact, you will be able to launch multiple balls. Each of them is moved by its own thread. In addition, the AWT event dispatch thread continues running in parallel, taking care of user interface events. Because each thread gets a chance to run, the main thread has the opportunity to notice when a user clicks the Close button while the balls are bouncing. The thread can then process the "close" action.

Here is a simple procedure for running a task in a separate thread:

1.

Place the code for the task into the run method of a class that implements the Runnable interface. That interface is very simple, with a single method:

 public interface Runnable {    void run(); } 

You simply implement a class, like this:


class MyRunnable implements Runnable
{
   public void run()
   {
      task code
   }
}

2.

Construct an object of your class:

 Runnable r = new MyRunnable(); 

3.

Construct a Thread object from the Runnable:

 Thread t = new Thread(r); 

4.

Start the thread.

 t.start(); 

To make our bouncing-ball program into a separate thread, we need only implement a class BallRunnable and place the code for the animation inside the run method, as in the following code:

 class BallRunnable implements Runnable {    . . .    public void run()    {       try       {          for (int i = 1; i <= STEPS; i++)          {             ball.move(component.getBounds());             component.repaint();             Thread.sleep(DELAY);          }       }       catch (InterruptedException exception)       {       }    }    . . . } 

Again, we need to catch an InterruptedException that the sleep method threatens to throw. We discuss this exception in the next section. Typically, a thread is terminated by being interrupted. Accordingly, our run method exits when an InterruptedException occurs.

Whenever the Start button is clicked, the addBall method launches a new thread (see Figure 1-2):

 Ball b = new Ball(); panel.add(b); Runnable r = new BallRunnable(b, panel); Thread t = new Thread(r); t.start(); 

Figure 1-2. Running multiple threads


That's all there is to it! You now know how to run tasks in parallel. The remainder of this chapter tells you how to control the interaction between threads.

The complete code is shown in Example 1-2.

NOTE

You can also define a thread by forming a subclass of the Thread class, like this:

 class MyThread extends Thread {    public void run()    {       task code    } } 

Then you construct an object of the subclass and call its start method. However, this approach is no longer recommended. You should decouple the task that is to be run in parallel from the mechanism of running it. If you have many tasks, it is too expensive to create a separate thread for each one of them. Instead, you can use a thread poolsee page 59.


CAUTION

Do not call the run method of the Thread class or the Runnable object. Calling the run method directly merely executes the task in the same threadno new thread is started. Instead, call the Thread.start method. It will create a new thread that executes the run method.


Example 1-2. BounceThread.java
   1. import java.awt.*;   2. import java.awt.event.*;   3. import java.awt.geom.*;   4. import java.util.*;   5. import javax.swing.*;   6.   7. /**   8.    Shows an animated bouncing ball.   9. */  10. public class BounceThread  11. {  12.    public static void main(String[] args)  13.    {  14.       JFrame frame = new BounceFrame();  15.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  16.       frame.setVisible(true);  17.    }  18. }  19.  20. /**  21.    A runnable that animates a bouncing ball.  22. */  23. class BallRunnable implements Runnable  24. {  25.    /**  26.       Constructs the runnable.  27.       @aBall the ball to bounce  28.       @aPanel the component in which the ball bounces  29.    */  30.    public BallRunnable(Ball aBall, Component aComponent)  31.    {  32.       ball = aBall;  33.       component = aComponent;  34.    }  35.  36.    public void run()  37.    {  38.       try  39.       {  40.          for (int i = 1; i <= STEPS; i++)  41.          {  42.             ball.move(component.getBounds());  43.             component.repaint();  44.             Thread.sleep(DELAY);  45.          }  46.       }  47.       catch (InterruptedException e)  48.       {  49.       }  50.    }  51.  52.    private Ball ball;  53.    private Component component;  54.    public static final int STEPS = 1000;  55.    public static final int DELAY = 5;  56. }  57.  58. /**  59.    A ball that moves and bounces off the edges of a  60.    rectangle  61. */  62. class Ball  63. {  64.    /**  65.       Moves the ball to the next position, reversing direction  66.       if it hits one of the edges  67.    */  68.    public void move(Rectangle2D bounds)  69.    {  70.       x += dx;  71.       y += dy;  72.       if (x < bounds.getMinX())  73.       {  74.          x = bounds.getMinX();  75.          dx = -dx;  76.       }  77.       if (x + XSIZE >= bounds.getMaxX())  78.       {  79.          x = bounds.getMaxX() - XSIZE;  80.          dx = -dx;  81.       }  82.       if (y < bounds.getMinY())  83.       {  84.          y = bounds.getMinY();  85.          dy = -dy;  86.       }  87.       if (y + YSIZE >= bounds.getMaxY())  88.       {  89.          y = bounds.getMaxY() - YSIZE;  90.          dy = -dy;  91.       }  92.    }  93.  94.    /**  95.       Gets the shape of the ball at its current position.  96.    */  97.    public Ellipse2D getShape()  98.    {  99.       return new Ellipse2D.Double(x, y, XSIZE, YSIZE); 100.    } 101. 102.    private static final int XSIZE = 15; 103.    private static final int YSIZE = 15; 104.    private double x = 0; 105.    private double y = 0; 106.    private double dx = 1; 107.    private double dy = 1; 108. } 109. 110. /** 111.    The panel that draws the balls. 112. */ 113. class BallPanel extends JPanel 114. { 115.    /** 116.       Add a ball to the panel. 117.       @param b the ball to add 118.    */ 119.    public void add(Ball b) 120.    { 121.       balls.add(b); 122.    } 123. 124.    public void paintComponent(Graphics g) 125.    { 126.       super.paintComponent(g); 127.       Graphics2D g2 = (Graphics2D) g; 128.       for (Ball b : balls) 129.       { 130.          g2.fill(b.getShape()); 131.       } 132.    } 133. 134.    private ArrayList<Ball> balls = new ArrayList<Ball>(); 135. } 136. 137. /** 138.    The frame with panel and buttons. 139. */ 140. class BounceFrame extends JFrame 141. { 142.    /** 143.       Constructs the frame with the panel for showing the 144.       bouncing ball and Start and Close buttons 145.    */ 146.    public BounceFrame() 147.    { 148.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 149.       setTitle("BounceThread"); 150. 151.       panel = new BallPanel(); 152.       add(panel, BorderLayout.CENTER); 153.       JPanel buttonPanel = new JPanel(); 154.       addButton(buttonPanel, "Start", 155.          new ActionListener() 156.          { 157.             public void actionPerformed(ActionEvent event) 158.             { 159.                addBall(); 160.             } 161.          }); 162. 163.       addButton(buttonPanel, "Close", 164.          new ActionListener() 165.          { 166.             public void actionPerformed(ActionEvent event) 167.             { 168.                System.exit(0); 169.             } 170.          }); 171.       add(buttonPanel, BorderLayout.SOUTH); 172.    } 173. 174.    /** 175.       Adds a button to a container. 176.       @param c the container 177.       @param title the button title 178.       @param listener the action listener for the button 179.    */ 180.    public void addButton(Container c, String title, ActionListener listener) 181.    { 182.       JButton button = new JButton(title); 183.       c.add(button); 184.       button.addActionListener(listener); 185.    } 186. 187.    /** 188.       Adds a bouncing ball to the canvas and starts a thread 189.       to make it bounce 190.    */ 191.    public void addBall() 192.    { 193.       Ball b = new Ball(); 194.       panel.add(b); 195.       Runnable r = new BallRunnable(b, panel); 196.       Thread t = new Thread(r); 197.       t.start(); 198.    } 199. 200.    private BallPanel panel; 201.    public static final int DEFAULT_WIDTH = 450; 202.    public static final int DEFAULT_HEIGHT = 350; 203.    public static final int STEPS = 1000; 204.    public static final int DELAY = 3; 205. } 


 java.lang.Thread 1.0 

  • Thread(Runnable target)

    constructs a new thread that calls the run() method of the specified target.

  • void start()

    starts this thread, causing the run() method to be called. This method will return immediately. The new thread runs concurrently.

  • void run()

    calls the run method of the associated Runnable.


 java.lang.Runnable 1.0 

  • void run()

    You must override this method and supply the instructions for the task that you want to have executed.



    Core JavaT 2 Volume II - Advanced Features
    Building an On Demand Computing Environment with IBM: How to Optimize Your Current Infrastructure for Today and Tomorrow (MaxFacts Guidebook series)
    ISBN: 193164411X
    EAN: 2147483647
    Year: 2003
    Pages: 156
    Authors: Jim Hoskins

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net