Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com
Answertopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions
Privacy Policy

  




 

 

Thinking in Java
Prev Contents / Index Next

Managing concurrency

When you make changes to any Swing component properties from the main method of your class or in a separate thread, be aware that the event dispatching thread might be vying for the same resources. [85]

The following program shows how you can get an unexpected result by not paying attention to the event dispatching thread:

//: c14:EventThreadFrame.java
// Race Conditions using Swing Components.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.bruceeckel.swing.Console;

public class EventThreadFrame extends JFrame {
  private JTextField statusField =
    new JTextField("Initial Value");
  public EventThreadFrame() {
    Container cp = getContentPane();
    cp.add(statusField, BorderLayout.NORTH);
    addWindowListener(new WindowAdapter() {
      public void windowOpened(WindowEvent e) {
        try { // Simulate initialization overhead
          Thread.sleep(2000);
        } catch (InterruptedException ex) {
          throw new RuntimeException(ex);
        }
        statusField.setText("Initialization complete");
      }
    });
  }
  public static void main (String[] args) {
    EventThreadFrame etf = new EventThreadFrame();
    Console.run(etf, 150, 60);
    etf.statusField.setText("Application ready");
    System.out.println("Done");
  }
} ///:~


It is easy to see what is supposed to happen. In the main method, a new EventThreadFrame class is created and run using the Console.run( ) method. After the frame has been created and run, the value of the text field is set to “Application ready,” and then, just before exiting main( ), “Done” is sent to the console.

When the frame is created, the text field is constructed with the value “Initial Value” in the constructor of the frame, and an event listener is added that listens for the opening of the window. This event will be received by the JFrame as soon as the setVisible(true) method has been called (by Console.run( )) and is the right place to do any initialization that affects the view of the window. In this example, a call to sleep( ) simulates some initialization code that might take a couple of seconds. After this is done, the value of the text box is set to “Initialization complete.”

You would expect that the text field would display “Initial Value” followed by “Initialization complete” and then “Application Ready.” Next the word “Done” should appear on the command prompt. What really happens is that the setText( ) method on the TextField is called by the main thread before the EventThreadFrame has had a chance to process its events. This means that the string “Application ready” might actually appear before “Initialization complete.” In reality, things might not even appear in this order. Depending on the speed of your system, the Swing event dispatching thread may already be busy handling the windowOpened event, so you won’t see the text field value until after that event, but by then the text will have been changed to “Initialization Complete.” Since the text field was set to this value last, the message “Application ready” is lost. To makes things worse, the word “Done” appears on the command prompt before anything else happens at all!

This undesirable and somewhat unpredictable effect is caused by the simple fact that there are two threads that need some sort of synchronization. It shows that you can sometimes get into trouble with threads and Swing. To solve this problem, you must ensure that Swing component properties are only ever updated by the event dispatch thread.

This is easier than it sounds, using one of Swing’s two mechanisms, SwingUtilities.invokeLater( ) and SwingUtilities.invokeandWait( ). They do most of the work, which means that you don’t have to do too much complicated synchronization or thread programming.

They both take runnable objects as parameters and drive the run( ) with the Swing event processing thread, after it has processed any pending events in the queue.

//: c14:InvokeLaterFrame.java
// Eliminating race Conditions using Swing Components.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.bruceeckel.swing.Console;

public class InvokeLaterFrame extends JFrame {
  private JTextField statusField =
    new JTextField("Initial Value");
  public InvokeLaterFrame() {
    Container cp = getContentPane();
    cp.add(statusField, BorderLayout.NORTH);
    addWindowListener(new WindowAdapter() {
      public void windowOpened(WindowEvent e) {
        try { // Simulate initialization overhead
          Thread.sleep(2000);
        } catch (InterruptedException ex) {
           throw new RuntimeException(ex);
        }
        statusField.setText("Initialization complete");
      }
    });
  }
  public static void main(String[] args) {
    final InvokeLaterFrame ilf = new InvokeLaterFrame();
    Console.run(ilf, 150, 60);
    // Use invokeAndWait() to synchronize output to prompt:
    // SwingUtilities.invokeAndWait(new Runnable() {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        ilf.statusField.setText("Application ready");
      }
    });
    System.out.println("Done");
  }
} ///:~


A Runnable anonymous inner class is passed to SwingUtilities.invokeLater( ), which calls the setText( ) method of the text field. This queues the runnable object as an event so that it is the event dispatching thread that calls the setText( ) method after first processing any pending events. This means that the windowOpening event will be processed before the text field displays “Application ready,” which is the intended result.

invokeLater( ) is asynchronous, so it returns right away. This can be useful because it doesn’t block, so your code runs smoothly. However, it doesn’t solve the problem with the “Done” string, which is still printed to the command prompt before anything else happens.

The solution to this problem is to use invokeAndWait( ) instead of invokeLater( ) to set the text field value to “Application Ready.” This method is synchronous, which means that it will block until the event has been processed before returning. The System.out.println(“Done”) statement will only be reached after the text field value has been set, so it will be the last statement to be executed. This gives us completely predictable and correct behavior.

Using invokeAndWait( ) provides one of the necessary conditions for deadlock, so make sure that you are careful about controlling shared resources if you are using invokeAndWait( ), especially if you are calling it from more than one thread.

You will probably use invokeLater( ) more often than invokeAndWait( ), but remember that if you set the properties of a Swing component any time after initialization, it should be done using one of these methods.
Thinking in Java
Prev Contents / Index Next


 
 
   Reproduced courtesy of Bruce Eckel, MindView, Inc. Design by Interspire