Easy to Learn Java: Programming Articles, Examples and Tips

Start with Java in a few days with Java Lessons or Lectures

Home

Code Examples

Java Tools

More Java Tools!

Java Forum

All Java Tips

Books

Submit News
Search the site here...
Search...
 

Chapter 2. (Introduction) Swing Mechanics. Easy for reading, Click here!

Chapter 2. (Introduction) Swing Mechanics. Easy for reading, Click here!

[ Return to Swing (Book) ]

Page: 3/14 


Previous Page Previous Page (2/14) - Next Page (4/14) Next Page
Subpages: 1. JComponent properties, size, and positioning 
2.  Event handling and dispatching 
3. Multithreading
4. Timers
5. AppContext service
6. Inside Timers & the TimerQueue
7. JavaBeans architecture
8. Fonts, Colors, Graphics and text
9. Using the Graphics clipping area
10. Graphics debugging
11. Painting and validation
12. Focus Management
13. Keyboard input, KeyStrokes, and Actions
14. SwingUtilities

2.3 Multithreading

To help us in ensuring that all our event handling code gets executed only from within the event-dispatching thread, Swing provides a very helpful class that, among other things, allows us to add Runnable objects to the system event queue. This class is called SwingUtilities and it contains two methods that we are interested in here: invokeLater() and invokeAndWait(). The first method adds a Runnable to the system event queue and returns immediately. The second method adds a Runnable and waits for it to be dispatched, then returns after it finishes. The basic syntax of each follows:

    Runnable trivialRunnable = new Runnable() {

      public void run() {

        doWork(); // do some work

      }

    };

    SwingUtilities.invokeLater(trivialRunnable);

    try {

      Runnable trivialRunnable2 = new Runnable() {

        public void run() {

          doWork(); // do some work

        }

      };

      SwingUtilities.invokeAndWait(trivialRunnable2);

    }

    catch (InterruptedException ie) {

      System.out.println("...waiting thread interrupted!");

    }

    catch (InvocationTargetException ite) {

      System.out.println(

        "...uncaught exception within Runnable's run()");

    }

Because these Runnables are placed into the system event queue for execution within the event-dispatching thread, we should be just as careful that they execute quickly, as any other event handling code. In the above two examples, if the doWork() method did something that takes a long time (like loading a large file) we would find that the application would freeze up until the load finishes. In time-intensive cases such as this, we should use our own separate thread to maintain responsiveness.

The following code shows a typical way to build our own thread to do some time-intensive work. In order to safely update the state of any components from inside this thread, we must use invokeLater() or invokeAndWait():

    Thread workHard = new Thread() {

      public void run() {

        doToughWork(); // do some really time-intensive work    

        SwingUtilities.invokeLater( new Runnable () {

          public void run() {

            updateComponents(); // update the state of component(s)

          }

        });

      }

    };

    workHard.start();

Note: invokeLater() should be instead of invokeAndWait() whenever possible. If we do have to use invokeAndWait(), we should make sure that there are no locks (i.e.synchronized blocks) held by the calling thread that another thread might need during the operation.

This solves the problem of responsiveness, and it does dispatch component-related code to the event-dispatching thread, but it still cannot be considered completely user-friendly. Normally the user should be able to interrupt a time-intensive procedure. If we are waiting to establish a network connection, we certainly don't want to continue waiting indefinitely if the destination no longer exists. In most circumstances the user should have the option to interrupt our thread. The following pseudocode code shows a typical way to accomplish this, where stopButton causes the thread to be interrupted, updating component state accordingly:

    Thread workHarder = new Thread() {

      public void run() {

        doTougherWork();

        SwingUtilities.invokeLater( new Runnable () {

          public void run() {

            updateMyComponents(); // update the state of component(s)

          }

        });

      }

    };

    workHarder.start();

    public void doTougherWork() {
      try {

        // [some sort of loop]

        // ...if, at any point, this involves changing

        // component state we'll have to use invokeLater

        // here because this is a separate thread.

        //

        // We must do at least one of the following:

        // 1. Periodically check Thread.interrupted()

        // 2. Periodically sleep or wait
        if (Thread.interrupted()) {
          throw new InterruptedException();
        }
        Thread.wait(1000);
      }
      catch (InterruptedException e) {
        // let somebody know we've been interrupted

        // ...if this involves changing component state

        // we'll have to use invokeLater here.
      }
    }

    JButton stopButton = new JButton("Stop");

    ActionListener stopListener = new ActionListener() {

      public void actionPerformed(ActionEvent event) {

        // interrupt the thread and let the user know the

        // thread has been interrupted by disabling the

        // stop button.

        // ...this will occur on the regular event dispatch thread

        workHarder.interrupt();

        stopButton.setEnabled(false);    

      }

    };

    stopButton.addActionListener(stopListener);

Our stopButton interrupts the workHarder thread when pressed. There are two ways that doTougherWork() will know whether workHarder, the thread it is executed in, has been interrupted. If it is currently sleeping or waiting, an InterruptedException will be thrown which we can catch and process accordingly. The only other way to detect interruption is to periodically check the interrupted state by calling Thread.interrupted().

This approach is commonly used for constructing and displaying complex dialogs, I/O processes that result in component state changes (such as loading a document into a text component), intensive class loading or calculations, waiting for messages or to establish a network connection, etc.

Reference: Members of the Swing team have written a few articles about using threads with Swing, and have provided a class called SwingWorker that makes managing the type of multithreading described here more convenient. See http://java.sun.com/products/jfc/tsc/archive/tech_topics_arch/threads/threads.html

2.3.1    Special cases

There are some special cases in which we do not need to delegate code affecting the state of components to the event-dispatching thread:

1. Some methods in Swing, although few and far between, are marked as thread-safe and do not need special consideration. Some methods are thread-safe but are not marked as such: repaint(), revalidate(), and invalidate().

2. A component can be constructed and manipulated in any fashion we like, without regard for threads, as long as it has not yet been realized (i.e. its has been displayed or a repaint request has been queued). Top-level containers (JFrame, JDialog, JApplet) are realized after any of setVisible(true), show(), or pack() have been called on them. Also note that a component is considered realized as soon as it is added to a realized container.

3. When dealing with Swing applets (JApplets) all components can be constructed and manipulated without regard to threads until the start() method has been called, which occurs after the init() method.

2.3.2    How do we build our own thread-safe methods?

This is quite easy. Here is a thread-safe method template we can use to guarantee this method's code only executes in the event-dispatching thread:

    public void doThreadSafeWork() {

      if (SwingUtilities.isEventDispatchThread()) {

        //

        // do all work here...

        //

      }

      else {

        Runnable callDoThreadSafeWork =  new Runnable() {

          public void run() {

            doThreadSafeWork();

          }

        };

        SwingUtilities.invokeLater(callDoThreadSafeWork);

      }

    } 

2.3.3    How do invokeLater() and invokeAndWait() work?

class javax.swing.SystemEventQueueUtilities [package private]

When SwingUtilities receives a Runnable object through invokeLater(), it passes it immediately to the postRunnable() method of a class called SystemEventQueueUtilities. If a Runnable is received through invokeAndWait(), first the current thread is checked to make sure that it is not the event-dispatching thread. (It would be fatal to allow invokeAndWait() to be invoked from the event-dispatch thread itself!) An error is thrown if this is the case. Otherwise, we construct an Object to use as the lock on a critical section (i.e. a synchronized block). This block contains two statements. The first sends the Runnable to SystemEventQueueUtilities' postRunnable() method, along with a reference to the lock object. The second waits on the lock object so the calling thread won't proceed until this object is notified--hence "invoke and wait."

The postRunnable() method first communicates with the private SystemEventQueue, an inner class of SystemEventQueueUtilities, to return a reference to the system event queue. We then wrap the Runnable in an instance of RunnableEvent, another private inner class. The RunnableEvent constructor takes a Runnable and an Object representing the lock object (null if invokeLater() was called) as parameters.

The RunnableEvent class is a subclass of AWTEvent, and defines its own static int event ID --  EVENT_ID. (Note that whenever we define our own event we are expected to use an event ID greater than the value of AWTEvent.RESERVED_ID_MAX.) RunnableEvent's EVENT_ID is AWTEvent.RESERVED_ID_MAX + 1000. RunnableEvent also contains a static instance of a RunnableTarget, yet another private inner class. RunnableTarget is a subclass of Component and its only purpose is to act as the source and target of RunnableEvents.

How does RunnableTarget do this? Its constructor enables events with event ID matching RunnableEvent's ID:

    enableEvents(RunnableEvent.EVENT_ID);

It also overrides Component's protected processEvent() method to receive RunnableEvents. Inside this method it first checks to see if the event passed as parameter is in fact an instance of RunnableEvent. If it is, it is passed to SystemEventQueueUtilities' processRunnableEvent() method (this occurs after the RunnableEvent has been dispatched from the system event queue.)

Now back to RunnableEvent. The RunnableEvent constructor calls its superclass (AWTEvent) constructor passing its static instance of RunnableTarget as the event source, and EVENT_ID as the event ID. It also keeps references to the given Runnable and lock object.

So in short: when invokeLater() or invokeAndWait() is called, the Runnable passed to them is then passed to the SystemEventQueueUtilities.postRunnable() method along with a lock object that the calling thread (if it was invokeAndWait()) is waiting on. This method first tries to gain access to the system event queue and then wraps the Runnable and the lock object in an instance of RunnableEvent.

Once the RunnableEvent instance has been created, the postRunnable() method (which we have been in this whole time) checks to see if it did successfully gain access to the system event queue. This will only occur if we are not running as an applet, because applets do not have direct access to the system event queue. At this point, there are two possible paths depending on whether we are running an applet or an application:

Applications:

Since we have direct access to the AWT Sytstem event queue we just post the RunnableEvent and return. Then the event gets dispatched at some point in the event-dispatching thread by being sent to RunnableTarget's processEvent() method, which then sends it to the processRunnableEvent() method. If there was no lock used (i.e. invokeLater() was called) the Runnable is just executed and we are done. If there was a lock used (i.e. invokeAndWait() was called), we enter a a synchronized block on the lock object so that nothing else can access that object when we execute the Runnable. Remember that this is the same lock object that the calling thread is waiting on from within SwingUtilities.invokeAndWait(). Once the Runnable finishes, we call notify on this object, which then wakes up the calling thread and we are done.

Applets:

SystemEventQueueUtilities does some very interesting things to get around the fact that applets do not have direct access to the system event queue. To summarize a quite involved workaround procedure, an invisible RunnableCanvas (a private inner class that extends java.awt.Canvas) is maintained for each applet and stored in a static Hashtable using the calling thread as its key. A Vector of RunnableEvents is also maintained and instead of manually posting an event to the system event queue, a RunnableCanvas posts a repaint() request. Then, when the repaint request is dispatched in the event-dispatching thread, the appropriate RunnableCanvas's paint() method is called as expected. This method has been constructed to locate any RunnableEvents (stored in the Vector) associated with a given RunnableCanvas, and execute them (somewhat of a hack, but it works).



[ Return to Swing (Book) ]


Search Books
Custom Search
Latest articles
 SSL with GlassFish v2, page 5

 SSL with GlassFish v2, page 4

 SSL with GlassFish v2, page 3

 SSL with GlassFish v2, page 2

 The Java Lesson 2: Anatomy of a simple Java program, page 2

 New site about Java for robots and robotics: both software and hardware.

 Exceptions -III: What's an exception and why do I care?

 Exceptions -II: What's an exception and why do I care?

 Exceptions: What's an exception and why do I care?

 Double your Java code quality in 10 minutes, here is receipt

 Murach's Java Servlets and JSP

 How to get ascii code from a char in Java?

 Can we just try without catch? Yes!

 Make Tomcat page load faster

 Make your Tomcat More secure - limit network address for certain IP addresses

 New Java book online starts now here...

 Implementing RESTful Web Services in Java

 Firefox trimming from 1 GB to 40 Mb with many tabs opened

 SSL with GlassFish v2

 My request to replublish Tech Tips

 Search JavaFAQ.nu site here

 New Advanced Installer for Java 6.0 brings XML updates and imports 3rd party MSI

 EJB programming restrictions

 Maven vs Ant or Ant vs Maven?

 Why Java does not use default value which it should?

 How to unsign signed bytes in Java - your guide is here

 The Java Lesson 3: Identifiers and primitive data types. Page 2

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 4

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 3

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 2


[ More in News Section ]


Home Code Examples Java Forum All Java Tips Books Submit News, Code... Search... Offshore Software Tech Doodling

RSS feed Java FAQ RSS feed Java FAQ News     

    RSS feed Java Forums RSS feed Java Forums

All logos and trademarks in this site are property of their respective owner. The comments are property of their posters, all the rest 1999-2006 by Java FAQs Daily Tips.

Interactive software released under GNU GPL, Code Credits, Privacy Policy