Multithreading with GUI

This program uses separate threads to modify the content displayed in a Swing GUI. The nature of multithreaded programming prevents the programmer from knowing exactly when a thread will execute. Swing components are not thread safeif multiple threads manipulate a Swing GUI component, the results may not be correct. All interactions with Swing GUI components should be performed as part of the event-dispatching thread (also known as the event-handling thread). Class SwingUtilities (package javax.swing) provides static method invokeLater to help with this process. Method invokeLater specifies GUI processing statements to execute later as part of the event-dispatching thread. Method invokeLater receives as its argument an object that implements interface Runnable. The method places the Runnable as an event into the event-dispatching thread's queue of events. These events are processed in the order they appear in the queue. Because only one thread handles these events, it can be guaranteed that the GUI will be updated properly.

Our next example (Fig. 23.17) demonstrates this concept. When this program calls invokeLater, the GUI component update will be queued for execution in the event-dispatching thread. The Runnable's run method will then be invoked as part of the event-dispatching thread to perform the output and ensure that the GUI component is updated in a thread-safe manner. This example also demonstrates how to suspend a thread (i.e., temporarily prevent it from executing) and how to resume a suspended thread.

Figure 23.17. RunnableObject outputs a random uppercase letter on a JLabel. Allows user to suspend and resume execution.

(This item is displayed on pages 1088 - 1090 in the print version)

 1 // Fig. 23.17: RunnableObject.java
 2 // Runnable that writes a random character to a JLabel
 3 import java.util.Random;
 4 import java.util.concurrent.locks.Condition;
 5 import java.util.concurrent.locks.Lock;
 6 import javax.swing.JLabel;
 7 import javax.swing.SwingUtilities;
 8 import java.awt.Color;
 9
10 public class RunnableObject implements Runnable
11 {
12 private static Random generator = new Random(); // for random letters
13 private Lock lockObject; // application lock; passed in to constructor
14 private Condition suspend; // used to suspend and resume thread 
15 private boolean suspended = false; // true if thread suspended 
16 private JLabel output; // JLabel for output
17
18 public RunnableObject( Lock theLock, JLabel label )
19 {
20 lockObject = theLock; // store the Lock for the application 
21 suspend = lockObject.newCondition(); // create new Condition
22 output = label; // store JLabel for outputting character
23 } // end RunnableObject constructor
24
25 // place random characters in GUI
26 public void run() 
27 {
28 // get name of executing thread 
29 final String threadName = Thread.currentThread().getName();
30
31 while ( true ) // infinite loop; will be terminated from outside
32 {
33 try
34 {
35 // sleep for up to 1 second
36 Thread.sleep( generator.nextInt( 1000 ) );
37
38 lockObject.lock(); // obtain the lock 
39 try
40 {
41 while ( suspended ) // loop until not suspended
42 {
43 suspend.await(); // suspend thread execution
44 } // end while
45 } // end try
46 finally
47 {
48 lockObject.unlock(); // unlock the lock
49 } // end finally
50 } // end try
51 // if thread interrupted during wait/sleep
52 catch ( InterruptedException exception )
53 {
54 exception.printStackTrace(); // print stack trace
55 } // end catch
56
57 // display character on corresponding JLabel
58 SwingUtilities.invokeLater( 
59 new Runnable() 
60 {
61 // pick random character and display it
62 public void run()
63 {
64 // select random uppercase letter
65 char displayChar =
66 ( char ) ( generator.nextInt( 26 ) + 65 );
67
68 // output character in JLabel
69 output.setText( threadName + ": " + displayChar );
70 } // end method run 
71 } // end inner class 
72 ); // end call to SwingUtilities.invokeLater
73 } // end while
74 } // end method run
75
76 // change the suspended/running state
77 public void toggle()
78 {
79 suspended = !suspended; // toggle boolean controlling state
80
81 // change label color on suspend/resume
82 output.setBackground( suspended ? Color.RED : Color.GREEN );
83
84 lockObject.lock(); // obtain lock
85 try
86 {
87 if ( !suspended ) // if thread resumed
88 {
89 suspend.signal(); // resume thread
90 } // end if
91 } // end try
92 finally
93 {
94 lockObject.unlock(); // release lock
95 } // end finally
96 } // end method toggle
97 } // end class RunnableObject

Class RunnableObject (Fig. 23.17) implements interface Runnable's run method (lines 2674). Line 29 uses static Thread method currentThread to determine the currently executing thread and Thread method getName to return the name of that thread. Every executing thread has a default name that includes the number of the thread (see the output of Fig. 23.18). Lines 3173 are an infinite loop. [Note: In earlier chapters we have said that infinite loops are bad programming because the application will not terminate. In this case, the infinite loop is in a separate thread from the main thread. When the application window is closed in this example, all the threads created by the main thread are closed as well, including threads (such as this one) that are executing infinite loops.] In each iteration of the loop, the thread sleeps for a random interval from 0 to 1 seconds (line 36).

Figure 23.18. RandomCharacters creates a JFrame with three RunnableObjects and three JCheckBoxes to allow the user to suspend and resume threads.

(This item is displayed on pages 1091 - 1093 in the print version)

 1 // Fig. 23.18: RandomCharacters.java
 2 // Class RandomCharacters demonstrates the Runnable interface
 3 import java.awt.Color;
 4 import java.awt.GridLayout;
 5 import java.awt.event.ActionEvent;
 6 import java.awt.event.ActionListener;
 7 import java.util.concurrent.Executors;
 8 import java.util.concurrent.ExecutorService;
 9 import java.util.concurrent.locks.Condition;
10 import java.util.concurrent.locks.Lock;
11 import java.util.concurrent.locks.ReentrantLock;
12 import javax.swing.JCheckBox;
13 import javax.swing.JFrame;
14 import javax.swing.JLabel;
15
16 public class RandomCharacters extends JFrame implements ActionListener
17 {
18 private final static int SIZE = 3; // number of threads
19 private JCheckBox checkboxes[]; // array of JCheckBoxes
20 private Lock lockObject = new ReentrantLock( true ); // single lock
21
22 // array of RunnableObjects to display random characters
23 private RunnableObject[] randomCharacters =
24 new RunnableObject[ SIZE ];
25
26 // set up GUI and arrays
27 public RandomCharacters()
28 {
29 checkboxes = new JCheckBox[ SIZE ]; // allocate space for array
30 setLayout( new GridLayout( SIZE, 2, 5, 5 ) ); // set layout
31
32 // create new thread pool with SIZE threads 
33 ExecutorService runner = Executors.newFixedThreadPool( SIZE );
34
35 // loop SIZE times
36 for ( int count = 0; count < SIZE; count++ )
37 {
38 JLabel outputJLabel = new JLabel(); // create JLabel
39 outputJLabel.setBackground( Color.GREEN ); // set color
40 outputJLabel.setOpaque( true ); // set JLabel to be opaque
41 add( outputJLabel ); // add JLabel to JFrame
42
43 // create JCheckBox to control suspend/resume state
44 checkboxes[ count ] = new JCheckBox( "Suspended" );
45
46 // add listener which executes when JCheckBox is clicked
47 checkboxes[ count ].addActionListener( this );
48 add( checkboxes[ count ] ); // add JCheckBox to JFrame
49
50 // create a new RunnableObject
51 randomCharacters[ count ] =
52 new RunnableObject( lockObject, outputJLabel );
53
54 // execute RunnableObject 
55 runner.execute( randomCharacters[ count ] );
56 } // end for
57
58 setSize( 275, 90 ); // set size of window
59 setVisible( true ); // show window
60
61 runner.shutdown(); // shutdown runner when threads finish
62 } // end RandomCharacters constructor
63
64 // handle JCheckBox events
65 public void actionPerformed( ActionEvent event )
66 {
67 // loop over all JCheckBoxes in array
68 for ( int count = 0; count < checkboxes.length; count++ )
69 {
70 // check if this JCheckBox was source of event
71 if ( event.getSource() == checkboxes[ count ] )
72 randomCharacters[ count ].toggle(); // toggle state
73 } // end for
74 } // end method actionPerformed
75
76 public static void main( String args[] )
77 {
78 // create new RandomCharacters object
79 RandomCharacters application = new RandomCharacters();
80
81 // set application to end when window is closed
82 application.setDefaultCloseOperation( EXIT_ON_CLOSE );
83 } // end main
84 } // end class RandomCharacters
 

When the thread awakens, line 38 acquires the Lock on this application. Lines 4144 loop while the boolean variable suspended is TRue. Line 43 calls method await on Condition suspend to temporarily release the Lock and place this thread into the waiting state. When this thread is signaled, it reacquires the Lock, moves back to the runnable state and releases the Lock (line 48). When suspended is false, the thread should resume execution. If suspended is still true, the loop executes again.

Lines 5872 call SwingUtilities method invokeLater. Lines 5971 declare an anonymous inner class that implements the Runnable interface, and lines 6270 declare method run. The method call to invokeLater places this Runnable object in a queue to be executed by the event-dispatching thread. Lines 6566 create a random uppercase character. Line 69 calls method setText on the JLabel output to display the thread name and the random character on the JLabel in the application window.

When the user clicks the JCheckBox to the right of a particular JLabel, the corresponding thread should be suspended (temporarily prevented from executing) or resumed (allowed to continue executing). Suspending and resuming of a thread can be implemented by using thread synchronization and Condition methods await and signal. Lines 7796 declare method toggle, which will change the suspended/resumed state of the current thread. Line 79 reverses the value of boolean variable suspended. Line 82 changes the background color of the JLabel by calling method setBackground. If the thread is suspended, the background color will be Color.RED, whereas if the thread is running, the background color will be Color.GREEN. Method toggle is called from the event handler in Fig. 23.18, so its tasks will be performed in the event-dispatch threadthus, there is no need to use invokeLater for line 82. Line 84 acquires the Lock for this application. Line 87 then tests whether the thread was just resumed. If this is true, line 89 calls method signal on Condition suspend. This method call will alert a thread that was placed in the waiting state by the await method call in line 43. Line 94 releases the Lock on this application inside a finally block.

Note that the if statement in line 87 does not have an associated else. If this condition fails, it means that the thread has just been suspended. When this happens, a thread executing at line 38 will enter the while loop and line 43 will suspend the thread with a call to method await.

Class RandomCharacters (Fig. 23.18) displays three JLabels and three JCheckBoxes. A separate thread of execution is associated with each JLabel and JCheckBox pair. Each thread randomly displays letters from the alphabet in its corresponding JLabel object. Line 33 creates a new ExecutorService with method newFixedThreadPool. Lines 3656 iterate three times. Lines 3841 create and customize the JLabel. Line 44 creates the JCheckBox, and line 47 adds an ActionListener for each JCheckBox (this will be discussed later.) Lines 5152 create a new RunnableObject implements the Runnable interface. Line 55 executes the RunnableObject with one of the threads of execution created in runner in line 33.

If the user clicks the Suspended checkbox next to a particular JLabel, the program invokes method actionPerformed (lines 6574) to determine which checkbox generated the event. Lines 6873 determine which checkbox generated the event. Line 71 checks whether the source of the event is the JCheckBox in the current index. If it is, line 72 calls method toggle (lines 7592 of Fig. 23.17) on that RunnableObject.

Introduction to Computers, the Internet and the World Wide Web

Introduction to Java Applications

Introduction to Classes and Objects

Control Statements: Part I

Control Statements: Part 2

Methods: A Deeper Look

Arrays

Classes and Objects: A Deeper Look

Object-Oriented Programming: Inheritance

Object-Oriented Programming: Polymorphism

GUI Components: Part 1

Graphics and Java 2D™

Exception Handling

Files and Streams

Recursion

Searching and Sorting

Data Structures

Generics

Collections

Introduction to Java Applets

Multimedia: Applets and Applications

GUI Components: Part 2

Multithreading

Networking

Accessing Databases with JDBC

Servlets

JavaServer Pages (JSP)

Formatted Output

Strings, Characters and Regular Expressions

Appendix A. Operator Precedence Chart

Appendix B. ASCII Character Set

Appendix C. Keywords and Reserved Words

Appendix D. Primitive Types

Appendix E. (On CD) Number Systems

Appendix F. (On CD) Unicode®

Appendix G. Using the Java API Documentation

Appendix H. (On CD) Creating Documentation with javadoc

Appendix I. (On CD) Bit Manipulation

Appendix J. (On CD) ATM Case Study Code

Appendix K. (On CD) Labeled break and continue Statements

Appendix L. (On CD) UML 2: Additional Diagram Types

Appendix M. (On CD) Design Patterns

Appendix N. Using the Debugger

Inside Back Cover



Java(c) How to Program
Java How to Program (6th Edition) (How to Program (Deitel))
ISBN: 0131483986
EAN: 2147483647
Year: 2003
Pages: 615

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