Java Threads

This page is old, and primarily directed at JDK1.1

Remote Places to go:

  • 9/01 java.sun.com technical article about threads and swing
  • Javasoft Thread Tutorial
  • The Swing Connection articles list - look for three articles about threads.
  • JDK 1.4.2 Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?
  • Java Threads, by Scott Oaks and Henry Wong.
  • Concurrent Programming in Java, by Doug Lea. - Online Supplement
  • Doug Lea's util.concurrent classes (J2SE 1.5 inludes much fo the functionality in util.concurrent)
  • JavaPP - Java Plug and Play - Research in Realtime and Embedded Systems and Formal Methods.
  • The JDK1.1.8 Solaris Native Threads pack README says:
    Specific information concerning multithreaded programming on Solaris may be found in the Multithreaded Programming Guide of the Solaris Software Developer AnswerBook. This information is also available at http://docs.sun.com. Navigate to the Tools link under the Programming heading and go to the link "Solaris 2.6 Software Developer Collection Vol. 1".
  • The JavaTM on Solaris 2.6 white paper contains a section on threading.
  • comp.programming.threads FAQ
  • Thread Monitor Applet (does not work with netscape4.0 and later, see the description) local copy - Local description - remote copy
  • Allen Holub wrote a series of articles for JavaWorld that became the book: Taming Java Threads The columns are from the Java Toolbox column:
  • Part 1 A Java programmers guide to threading architectures
  • Part 2 The perils of race conditions, deadlock, and other threading problems
  • Part 3 Roll-your-own mutexes and centralized lock management
  • Part 4 Condition variables and counting semaphores -- filling in a few chinks in Java's threading model
  • Part 5 Has Sun abandoned 'run anywhere'? Plus: Threads and Swing, timers, and getting around stop(), suspend(), and resume() deprecation
  • Part 6 The Observer pattern and mysteries of the AWTEventMulticaster
  • Part 7 Singletons, critical sections, and reader/writer locks
  • Part 8
  • Part 9 More threads in an object-oriented world: Synchronous dispatchers, active objects, detangling console I/O
  • Information about Runtime.exec: JDC Tech Tip "INVOKING PROGRAMS FROM JAVA APPLICATIONS JDC ExecDemo.java
  • Contents:
  • Common Threading Problems
  • Scheduler Platform Dependencies
  • Deadlock
  • Lock Starvation
  • Priorities
  • Priority Inversion
  • Swing
  • Common Thread Code Fragments
  • Tools: threadmon - jdb - HotJava - Netscape

  • Common Threading Problems

    There are a many common threading errors, below we discuss some of them.

    Scheduler Platform Dependencies

    Unfortunately Java does not specify how threads should be scheduled, instead this is left up to the platform or implementor.

    Under Windows '95 and NT, the operating system uses time-slicing to determine which thread should run.

    If two threads have the highest priority of all the threads, and the priorities are the same, then a time-slicing system will alternate between the two threads, and a non-time-slicing system will pick one thread and run it until completion.

    So, if you create two threads with the same priorities and run them, you may see different behaviour on different platforms.

    Under Solaris, the situation is even more confusing, because JDK1.1.4 has an optional native threads pack that uses time-slicing. So, under Solaris JDK1.1.4, if the native threads pack is installed, and the THREADS_FLAG is set to native, then you get time-slicing. If THREADS_FLAG is set to green, then you do not get time-slicing.

    The Javasoft Java tutorial Thread Priority page has two Java files that can be run as an application:

  • RaceTest.java
  • SelfishRunner.java
  • If you download and compile the classes, you can run with:
    java RaceTest
    
    Under a time-sliced system, such as Windows NT, the calls to Thread #0 ad Thread1 will be intermingled, but not necessarily alternating:
    bash$ java -classpath .\;/jdk1.1.3/lib/classes.zip RaceTest
    Thread #0, tick = 50000
    Thread #1, tick = 50000
    Thread #0, tick = 100000
    Thread #0, tick = 150000
    Thread #1, tick = 100000
    Thread #0, tick = 200000
    Thread #1, tick = 150000
    Thread #1, tick = 200000
    Thread #0, tick = 250000
    Thread #1, tick = 250000
    Thread #0, tick = 300000
    Thread #1, tick = 300000
    Thread #0, tick = 350000
    Thread #1, tick = 350000
    Thread #0, tick = 400000
    Thread #1, tick = 400000
    
    Under a non-time sliced system such as Solaris with non-native threads, you will get something like:
    cxh@brahe 83% setenv THREADS_FLAG green
    cxh@brahe 84% java RaceTest
    Thread #0, tick = 50000
    Thread #0, tick = 100000
    Thread #0, tick = 150000
    Thread #0, tick = 200000
    Thread #0, tick = 250000
    Thread #0, tick = 300000
    Thread #0, tick = 350000
    Thread #0, tick = 400000
    Thread #1, tick = 50000
    Thread #1, tick = 100000
    Thread #1, tick = 150000
    Thread #1, tick = 200000
    Thread #1, tick = 250000
    Thread #1, tick = 300000
    Thread #1, tick = 350000
    Thread #1, tick = 400000
    cxh@brahe 85%
    
    Under Solaris with JDK1.1.4 and native threads, we get time-slicing.
    cxh@brahe 87% setenv THREADS_FLAG native
    cxh@brahe 88% java RaceTest
    Thread #0, tick = 50000
    Thread #0, tick = 100000
    Thread #1, tick = 50000
    Thread #0, tick = 150000
    Thread #1, tick = 100000
    Thread #0, tick = 200000
    Thread #1, tick = 150000
    Thread #1, tick = 200000
    Thread #0, tick = 250000
    Thread #0, tick = 300000
    Thread #1, tick = 250000
    Thread #1, tick = 300000
    Thread #0, tick = 350000
    Thread #0, tick = 400000
    Thread #1, tick = 350000
    Thread #1, tick = 400000
    cxh@brahe 9% 
    
    If you run the applet version of RaceTest, then you will probably see output similar to the time-slicing output. I believe that this is because the applet has multiple threads running.

    As you can see, whether time-slicing occurs depends on the operating system and the version of Java that is running. Well designed programs should not depend on time-slicing being present or absent.

    Thread.yield()

    The following classes demonstrate how to use Thread.yield() to have the threads alternate:

  • PoliteRunner.java
  • RaceTest2.java
  • java RaceTest2
    
    Under Solaris, the output should be like:
    cxh@brahe 103% java RaceTest2
    Thread #0, tick = 50000
    Thread #1, tick = 50000
    Thread #0, tick = 100000
    Thread #1, tick = 100000
    Thread #0, tick = 150000
    Thread #1, tick = 150000
    Thread #0, tick = 200000
    Thread #1, tick = 200000
    Thread #0, tick = 250000
    Thread #1, tick = 250000
    Thread #0, tick = 300000
    Thread #1, tick = 300000
    Thread #0, tick = 350000
    Thread #1, tick = 350000
    Thread #0, tick = 400000
    Thread #1, tick = 400000
    cxh@brahe 104% 
    
    If you run the applet version of RaceTest2, then the output should have the threads alternating.

    Time-slicing and efficiency

    Note that time-slicing is not always more efficient than non-time-slicing. If individual threads are producing results that are useful, then time-slicing can result in a slower average time to completion.

    If we have 4 threads that take 5 seconds each to produce results, then under time-slicing, all 4 threads should finish in 4*5 seconds, so the average time to get results is 20 seconds.

    If we don't have time-slicing, then the first thread returns after 5 seconds, the second after 10 seconds, the third after 15 seconds and the fourth after 20 seconds. So the average time is 12.50 seconds.


    Deadlock

    A simple definition of deadlock is that a system is deadlocked if two or more threads are waiting for two or more locks to to be freed and the system is such that the locks will never be freed.

    The classic example of deadlock is the dining philosophers. The idea is that there are a bunch of philosophers sitting around a table. There is a bowl of rice in fron of each philosopher, and a single chopstick between each philosopher. A philosopher must have two chopsticks in order to eat. Once a philosopher has eaten, they put down both chopsticks so that another can eat.

    The problem is that if each philosopher picks up the chopstick on the left, then no philosopher will be able to get to chopsticks, so they will literally starve because of deadlock :-)

    The Ptolemy II Communicating Sequential Processes domain includes a Dining Philosophers Demo

    The Javasoft tutorial includes a dining philosophers applet.

    Java Threads, p 166-167 discusses some rules of thumb that help avoid deadlock:

  • Avoid having synchronized methods calling other synchronized methods.
  • Unfortunately, this is impractical, lots of Java methods are synchronized.
  • Actually if the synchronized method that is being called from a synchronized method does not call another synchronized method, then deadlock cannot occur (Is this true?)
  • Lock a "larger" object that is related to a set of smaller objects that need locks.
  • A gross example would be to lock all the data structures in a system with one lock. Unfortunately, this could prevent other threads from doing useful work.
  • Always acquire locks in the same order.
  • This can be tricky to implement, but the idea is that you have a lock hierarchy where each lock has only one parent and only one child. To try to acquire a lock, you must have the parent lock first. Unfortunately, there is no support in Java for this, it is up to the programmer.

  • Lock Starvation

    CPU starvation occurs when a thread never becomes the currently running thread. A thread can starve if it has a lower priority than the other threads and it never becomes the current thread. CPU starvation is usually a scheduling issue that occurs because the scheduler does not increase the priority of a thread that has not run on the CPU.
    Lock starvation occurs when a thread becomes the currently running thread, but the thread never obtains a lock it needs because another thread is holding the lock. Lock starvation is rare, each of the points must be true:
  • More than one thread must be trying to acquire the same lock.
  • The intermediate results during lock contention are of interest.
    If we are only interested in the final results, then we don't really care if some of the threads are starved of the lock. Eventually all the threads will get the lock, and we will get our results.
  • The threads must have the same priority.
  • A round-robin scheduler must be controlling the threads.
    If the threads are not under a round-robin scheduler, then we will have CPU starvation, not lock starvation.
  • We get lock starvation because:
  • Lock acquisitions do not queue - If thread is attempting to acquire a lock, it does not check to see if another thread has tried to acquire the lock and failed.
  • When a lock is released, the current thread may or may not continue to be the current thread.

  • Priorities

    The Java Specification, section 17.12 says that Thread priorities are a preference:
    Every thread has a priority. When there is competition for processing resources, threads with higher priority are generally executed in preference to threads with lower priority. Such preference is not, however, a guarantee that the highest priority thread will always be running, and thread priorities cannot be used to reliably implement mutual exclusion.
    So, be careful about using priorities.

    Priority Inversion

    Another subtle problem is priority inversion, which we need to find a good description of.

    Swing

    In Swing, components should only be accessed from the event-dispatching thread.

    Resourced

  • Javasoft Swing Tutorial: Threads and Swing
  • ArticleIndex in the Swing Connection contains a few articles about Threads and Swing.

  • Common Thread Code Fragments

    Below are some common thread code fragments that appear over and over again.

    Starting a thread

    In an applet, the code below will start a thread
    public void start() {
        if (_plotThread == null) {
            _plotThread = new Thread(this, "Plot");
            _plotThread.start();
        }
    }    
    
    If the thread is null, then either the thread was never started, or started and stopped. If the thread is null, we create a new thread and start it. If the thread is not null in an applet, then we have already started the thread

    Stopping a thread

    public void stop () {
    	_plotThread = null;
    }
    
    We set the thread to null here, but we could just call _plotThread.stop(). The Javasoft Thread tutorial says that stopping a thread is rather drastic, if the thread is doing something critical, then the applet might be left in an inconsistant state. Calling stop on a thread can be somewhat like hitting Control-C on a program - the program might not cleanup properly before exiting.

    Applets that use threads should implement the stop() method so that when the user moves to another page the applet stops. This is especially important for threads that produce sound or consume cpu cycles. See the Javasoft tutorial applet checklist for more information.

    Running

    public void run () {
        while (Thread.currentThread() == _plotThread) {
            repaint();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e){
        }
    }
    
    Here, we check to see if the _plotThread has been set to null by stop(). If it is not null, then we call repaint, and sleep for one second.

    When we sleep, other threads can run.


    Tools

    threadmon

    The Thread Monitor Applet ( local copy - remote copy) will display the threads running in a browser. Unfortunately, it only works with Netscape3.x or IE3.x, it will not work with Netscape4.x or IE4.x

    Apparently, in JDK1.0.2, which is what Netscape3.x was based on, applets could see each other's threads and the threads in the system thread group. In JDK1.1 this is not the case. Apparently, it was possible to write an applet that would call stop() on all the threads in other applets.

    Here's a simple demo of the Thread Monitor Applet that will only work under Netscape 3.x or IE3.x

    1. Start up Netscape 3.x or IE3.x
    2. Go to the Thread Monitor Applet and start the applet.
    3. Go to the Thread Debug Demo applet and start it.
    4. Now you can change the priorities of the running threads. Note that there are some strange behaviours, such as only one of the threads can be suspend.

    jdb

    jdb is the Java debugger that comes with Sun's JDK.
  • The jdb documentation ( Solaris and Linux, Windows) discusses how to run jdb.
  • There is a brief jdb applet debugging tutorial for JDK 1.3
  • Unfortunately, in JDK1.1.4, appletviewer -debug does not work:

    cxh@carson 39% appletviewer -debug Blink.html
    I/O exception while reading: /export/carson/carson2/cxh/pt/tycho/java/devel/blink/-debug
    Make sure that -debug is a file and is readable.
    
    This is Sun Java bug #4087784

    One workaround is to call jdb like so:

    cxh@carson 41% jdb sun.applet.AppletViewer Blink.html
    Initializing jdb...
    0xee322068:class(sun.applet.AppletViewer)
    > run
    run sun.applet.AppletViewer Blink.html 
    running ...
    main[1] 
    sun.applet.AppletViewer exited
    cxh@carson 42% 
    

    jdb and Emacs

    The jde package is an Emacs package that provides many features, including an interface to jdb. The jde package is listed on the Java IDE page (ftp site)

    jde is installed on the local cluster, to run it, type:

    M-x load-file /usr/tools/gnu/lib/emacs/ohm-lisp/jde-init.el
    
    To run the debugger, you have to type
    M-x jde-db
    
    jde-init.el supplies a hacked up version of the jdb defun so that M-x jdb sort of works.

    jde bugs and nits:

  • You must run it in an emacs that is running as an X11 client, or you will get the message:
    X windows are not in use or not initialized
    
    The fix is to edit speedbar.el and wrap the (if (x-display-color-p sexp at the end of the file with
    (condition-case ()
        (if (x-display-color-p)
        ...
      ) ;; monochrome
    (error nil))
    
    
  • When debugging, the jde package has problems tracking down files that are in different levels of the current package.
  • If you try to go to a frame of a file that you do not have source for, such as sun.applet.Appletviewer, then the emacs process filter crashes.

  • HotJava

    HotJava is Sun's Java capable browser It can be downloaded for individual, non-commercial use from http://java.sun.com/products/hotjava.

    With regard to threads, the key feature of HotJava1.1 is that you can monitor threads by selecting the View menu, then Monitor, then Threads.

    The HotJava help for threads says:

    Each applet running in HotJava has its own thread group, whether or not it actually creates any threads. All the threads created by the applet belong to that applet's thread group.

    In the Thread Monitor, a thread group (or applet group) is preceded either by +, which means the group is expanded to list all of the threads within it, or by >, which means the thread group can be expanded. Double-click on the thread group to toggle between these two states.

    When you select an applet thread group or an individual thread within an applet thread group, the Kill button is activated to let you kill that thread or thread group. When you select an individual applet thread, you can also use the Raise priority and Lower prioritybuttons to raise and lower the thread priorities one level at a time.

    BTW - A common method of debugging applets is to use System.out.println to print debugging messages. The HotJava FAQ says that to view System.out.println output, start HotJava with
    hotjava -log /dev/tty
    
    You can also set the HOT_JAVALOG environment variable. See the FAQ for information about running HotJava under Windows.

    Netscape

    Under Netscape4.0, you can dump thread info to a file by typing a t while in the Java Console.

    Getting Thread information

  • Ptolemy II includes Ptolemy II which will pretty print the threads. See $PTII/util/testsuite/PrintThreads.java
  • You can use Tcl to look at the threads with
    # Print info about threads, see also $PTII/util/testsuite/PrintThreads.java
    proc jdkThreads {} {
        set currentThread [java::call Thread currentThread]
        set parent [$currentThread getThreadGroup]
        puts "ThreadGroup:      [$parent toString]"
    
        # Figure out the root ThreadGroup
        while { ! [java::isnull $parent] } {
    	set rootGroup $parent
    	set parent2 [$parent getParent]
    	set parent $parent2
        }
        puts "Root ThreadGroup: [$rootGroup toString]"
    
        set threads [java::new {Thread[]} [$rootGroup activeCount]]
        $rootGroup enumerate $threads
    
        puts "Current Threads are:"
        for { set i 0} { $i < [$threads length]} {incr i} {
    	set thread [$threads get $i]
    	set currentThreadString ""
    	if {$thread == $currentThread} {
    	    set currentThreadString "          (Current Thread)"
    	} 
    	puts "$i. [$thread toString] $currentThreadString"
        }
        puts "To get a stack trace for each thread:"
        puts " Under Unix, try 'kill -3 pid', where pid is the process id from ps"
        puts " Under Windows, try Control-Break"
    }
    
    
    
    http://java.sun.com/j2se/1.4.1/changes.html says:
    A deadlock detection utility has been added to the Java HotSpot VM. The utility is invoked by a ctrl+\ (on Linux or the Solaris Operating Environment) or a ctrl-break (on Microsoft Windows) on the command line while an application is running. The utility detects Java-platform-level deadlocks, including locking done from the Java Native Interface (JNI), the Java Virtual Machine Profiler Interface (JVMPI), and Java Virtual Machine Debug Interface (JVMDI).

    When invoked, the utility displays a thread dump to standard out and indicates any Java-platform-level deadlocks it detects. Refer to this sample output. If the application is deadlocked because two or more threads are involved in a cylce to acquire monitors, then the list of such threads and monitors involved in the deadlocks are displayed. Note, however, that this will not find deadlocks involving threads waiting on monitors on which no signal will be forthcoming.

    The following non-standard command-line flags are recognized by the VM.

  • -XX:+UseAltSigs - On the Solaris operating environment, the VM uses SIGUSR1 by default, which can sometimes conflict with applications that signal-chain SIGUSR1. -XX:+UseAltSigs will cause the VM to use signals other than SIGUSR1 and SIGUSR2 as the default. Available beginning in J2SE 1.4.1 on the Solaris Operating Environment.
  • -XX:+UseConcMarkSweepGC - This flag turns on concurrent garbage collection. This collector executes mostly concurrently with the application. It trades the utilization of processing power that would otherwise be available to the application for shorter garbage collection pause times.
  • -XX:+UseParallelGC - This flag enables garbage collection to occur on multiple threads for better performance on multiprocessor machines.
  • Each of these new flags is non-standard, as indicated by the fact that they being with -XX, which means that they are subject to change without notice.


  • Back to My Java Page

    Last updated: %G% comments to: cxh at eecs