CheckThread.org (Beta)

Thread Confinement Example

Can you see any problems with the following Java code?

import javax.swing.JButton;
import java.awt.Color;

public class SomeClass {
  
    //Invoked on the main thread
    public void doSwingStuff(JButton jButton) {
        jButton.setBackground(Color.RED);
    }
}

The comment of the method doSwingStuff() says it runs on the main thread, but the method JButton.setBackground() should only by invoked from the event-dispatch thread since it is a Swing component. Invoking Swing methods on the wrong thread can lead to wacky concurrency failure modes like painting issue.

The Swing concurrency policy is complex: a Swing method is thread confined only when the Swing component is "realized" in the component hierarchy.

While this may seem like a blatant bug through visual inspection. in real world industry applications with thousands of lines of code, a bug like this is very difficult to catch. Moreover, the failure mode for calling a Swing method on the wrong thread could be a subtle painting issue or may go unnoticed on certain platforms until deployed to customers.

Example: Catching the Bug at Compile Time

With CheckThread, calling a Swing method on the wrong thread can be caught at compile time by adding annotations and running the static analysis tool.

import javax.swing.JButton;
import java.awt.Color;
import org.checkthread.annotations.ThreadSafe;


public class SomeClass {
  
    @ThreadSafe
    // Could also use @NotThreadSafe or @ThreadConfined annotation	
    public void doSwingStuff(JButton jButton) {
	
        //ERROR: Caught by CheckThread at compile time
        jButton.setBackground(Color.RED);
    }
}

Whether to use the @ThreadSafe, @NotThreadSafe, or the @ThreadConfined annotation on the doSwingStuff() method depends on the application and requirements.

Ask yourself - as the implementer of the doSwingStuff() method, what is the threading policy contract between my method and client code calling the method? Do I want client code to have to dispatch onto a particular runtime thread? Do I want client to not have to worry about threading?

The larger concurrency design of your application should drive this type of decision. It does require careful design, see the Literature References for learning more about concurrency.

Example: Calling Swing Correctly

The previous bug is fixed by dispatching to the EDT thread using SwingUtilities.invokeLater(...). CheckThread will report no errors when performing static analysis since the run() method is marked EDT thread confined.

import javax.swing.JButton;
import java.awt.Color;
import org.checkthread.annotations.*;


public class SomeClass {

    @ThreadSafe
    public void foo(final JButton jButton) {	

        SwingUtilities.invokeLater(new Runnable() {
		
            //OKAY: Invoking on correct thread
            @ThreadConfined(ThreadName.EDT)
            public void run() {
                jButton.setBackground(Color.RED);				
            }
        });
     }
}

 

Example: Generic Thread Confinement

Swing certainly isn't the only API that requires thread confinement. SWT, Android, and many other UI toolkits require certain interactions to occur on a specific thread. While it may seem like a short coming for a UI toolkit to require thread confinement, it is often a reasonable design decision and the lesser ot two evils. The simplicity in creating a thread confined UI toolkit far outweights the complexity required in creating a thread safe UI toolkit.

The most general case for thread confinement is an API which requires invocation to occur on a specific thread. In the example below, the method runOnWorkerThread() is thread confined by design.

import java.util.concurrent.*;
import org.checkthread.annotations.*;

public class GenericThreadConfinement {

    @ThreadConfined(ThreadName.MAIN)
    public void doStuff() {
        ExecutorService service = Executors.newSingleThreadExecutor();

        //Dispatch onto worker thread
        service.execute(new Runnable() {

            @ThreadConfined("myWorkerThread")
            public void run() {
			
                //OKAY: Invoking on correct thread
                runOnWorkerThread();
            }
        });

        //BUG: Invoking on wrong thread. 
        //Caught by CheckThread at compile time
        runOnWorkerThread();

    }

    //This method should only be called on 
    //the worker thread
    @ThreadConfined("myWorkerThread")
    public void runOnWorkerThread() {
        System.out.println("Hello World");
    }
}
©2009 CheckThread