Java’s threads are essential for building complex applications, but thread control is split across several classes in different packages added at different times in the JDK’s history. This tip shows how to connect these classes together to find threads and thread groups, and get thread information.
Table of Contents
- IntroductionGetting Thread Groups
- Getting the root thread groupGetting a list of all thread groupsGetting a thread group by name
Getting Threads
- Getting a list of all threadsGetting a list of all threads in a thread groupGetting a list of all threads in high-to-low priority orderGetting a list of all daemon threadsGetting a list of all threads in a specific stateGetting a list of all thread IDsGetting a thread by IDGetting a thread by name
Getting Thread Info
- Getting a list of all thread infosGetting thread info by thread IDGetting thread info by threadGetting thread info by thread nameGetting a thread by thread info
Getting Monitor and Lock Info
- Getting the thread that’s locking an objectGetting the object that’s blocking a threadGetting the thread that owns the object that’s blocking a thread
Downloads
Introduction
The principal threading classes areThread
andThreadGroup
injava.lang
. Java 5 added theThreadInfo
andThreadMXBean
classes injava.lang.management
to get state information and CPU usage for threads. Java 6 expanded these to get information on locks held by threads. Java 5 also addedjava.util.concurrent
classes for managing thread pools and creating special types of locks.
There is a lot of functionality here, but there are some gaps. For example, though thread groups are organized as a tree of groups within groups, there is no method to get the tree’s rootThreadGroup
. Though threads have names and IDs, there are no methods to search for them by name or ID. Methods onThreadInfo
give you important state and locking information about a thread, but there is no method to get aThreadInfo
for aThread,
or to get theThread
described by aThreadInfo
. And so on.
These functionality gaps are not fatal, just mildly annoying. Below are utility methods to help list and find threads, thread groups, and thread info. These utility methods build upon each other. To distinguish methods in this article from those in the JDK, I’llhighlightthe article’s methods.
AThreadUtilities
class including all of these methods is available fordownload. The source is released under the Lesser GPL, version 2.
Getting Thread Groups
EveryThread
is in a namedThreadGroup
. Thread groups are used to organize related threads and restrict thread access. Threads in a group can interrupt the group’s threads and set the group’s maximum priority. But, threads can’t access any other groups.
Thread groups are arranged hierarchically in a tree with groups containing groups containing groups. The root group of the tree is the JDK’s "system" group containing administrative threads, like those for object finalizing and signal dispatching. The "main" group under the "system" group contains the main application thread and threads it creates.
Unfortunately, there’s no method to get the root thread group or to search the thread group tree for a named group. Getting a list of all thread groups is also a bit tricky.
Getting the root thread group
To get the root thread group, first get the current thread and its thread group. Then get its parent group, then its parent group, and on up until you find a group with a null parent. That’s the rootThreadGroup
.
ThreadGroup rootThreadGroup = null; ThreadGroup getRootThreadGroup( ) { if ( rootThreadGroup != null ) return rootThreadGroup; ThreadGroup tg = Thread.currentThread( ).getThreadGroup( ); ThreadGroup ptg; while ( (ptg = tg.getParent( )) != null ) tg = ptg; return tg;}
Since the same root thread group is used for the life of the JVM, you can safely cache it for faster future use.
Getting a list of all thread groups
Theenumerate()
method on aThreadGroup
lists that group’s child groups. Pass atrue
second argument and it will recursively traverse the group and children to fill a given array withThreadGroup
objects. Start at the rootThreadGroup
and you’ll get a list of all thread groups,except the root thread group. Since method only lists the descendants of the root, you’ll have to add the root to this list yourself.
This sounds simple enough, but if the array you pass toenumerate()
is too small, the method silently drops some groups. To allocate an array the right size, you could use theactiveGroupCount()
method on aThreadGroup
but it returns the number of groupsin that group only, not in total. There’s no method to get the total number of thread groups. Even if there were such a method, it could be wrong a moment later if other threads add or destroy thread groups.
Instead, you’ll have to make a guess at the right array size then callenumerate()
. The method returns the number of groups it added to your array. If that number equals your array size, some groups might have been silently dropped. Increase the array size and try again.
ThreadGroup[] getAllThreadGroups( ) { final ThreadGroup root = getRootThreadGroup( ); int nAlloc = root.activeGroupCount( ); int n = 0; ThreadGroup[] groups; do { nAlloc *= 2; groups = new ThreadGroup[ nAlloc ]; n = root.enumerate( groups, true ); } while ( n == nAlloc ); ThreadGroup[] allGroups = new ThreadGroup[n+1]; allGroups[0] = root; System.arraycopy( groups, 0, allGroups, 1, n ); return allGroups;}
Getting a thread group by name
To find a named thread group, you’ll have to search the thread group tree. You can search recursively starting at the root thread group, but it’s easier to loop through the array returned by thegetAllThreadGroups()
method above. Note that thread groups may not have unique names. This search will stop on the first group that matches.
ThreadGroup getThreadGroup( final String name ) { if ( name == null ) throw new NullPointerException( "Null name" ); final ThreadGroup[] groups = getAllThreadGroups( ); for ( ThreadGroup group : groups ) if ( group.getName( ).equals( name ) ) return group; return null;}
Getting Threads
EveryThread
has a name and a unique long integer ID. The JDK’s own threads have names like "Finalizer" and "Reference Handler". When applications don’t name their threads, threads are automatically named "Thread-0", "Thread-1", and so on.
There’s no method to get aThread
based upon its ID or name. And getting a list of allThread
objects has the same difficulties as getting a list of allThreadGroup
objects did above.
Getting a list of all threads
Anotherenumerate()
method on aThreadGroup
lists that group’s threads. With atrue
second argument, it will recursively traverse the group to fill a given array withThread
objects. Start at the rootThreadGroup
and you’ll get a list of all threads in the JVM.
The problem here is the same as that for listing thread groups. If the array you pass toenumerate()
is too small, some threads might be silently dropped from the returned array. So, you’ll need to take a guess at the array size, callenumerate(),
check the returned value, and try again if the array filled up. To get a good starting guess, look to thejava.lang.management
package. TheManagementFactory
class there returns aThreadMXBean
who’sgetThreadCount()
method returns the total number of threads in the JVM. Of course, this can change a moment later, but it’s a good first guess.
Thread[] getAllThreads( ) { final ThreadGroup root = getRootThreadGroup( ); final ThreadMXBean thbean = ManagementFactory.getThreadMXBean( ); int nAlloc = thbean.getThreadCount( ); int n = 0; Thread[] threads; do { nAlloc *= 2; threads = new Thread[ nAlloc ]; n = root.enumerate( threads, true ); } while ( n == nAlloc ); return java.util.Arrays.copyOf( threads, n );}
Getting a list of all threads in a thread group
Listing the threads in just one thread group requires most of the same steps used above to get all threads in the JVM. The group’senumerate()
method fills your array withThread
objects, but you have to allocate the array at the right size or threads might be silently dropped. CallactiveCount()
on the group to get the number of threads in the group at that instant. Then use a larger size in case threads are added in the mean time.
Thread[] getGroupThreads( final ThreadGroup group ) { if ( group == null ) throw new NullPointerException( "Null thread group" ); int nAlloc = group.activeCount( ); int n = 0; Thread[] threads; do { nAlloc *= 2; threads = new Thread[ nAlloc ]; n = group.enumerate( threads ); } while ( n == nAlloc ); return java.util.Arrays.copyOf( threads, n );}
Getting a list of all threads in high-to-low priority order
Every thread has a numeric priority. The higher the priority, the more CPU time the thread is given. To get a list of all threads in priority order, start with thegetAllThreads()
method shown earlier. Then sort the list on thread priority. Since threads can change their priority at any time, the result is only approximately in high-to-low order.
Thread[] getAllThreadsPrioritized( ) { final Thread[] allThreads = getAllThreads( ); Arrays.sort( allThreads, new java.util.Comparator<Thread>( ) { public int compare( final Thread t1, final Thread t2 ) { return t2.getPriority( ) - t1.getPriority( ); } } ); return allThreads;}
Getting a list of all daemon threads
Daemons are background threads doing tasks to support the application. When the last non-daemon thread dies, daemon threads are stopped automatically and the JVM exits. TheisDaemon()
method onThread
tells you if a thread is a daemon.
TheThreadMXBean
class has agetDaemonThreadCount()
method that returns the current number of daemon threads, but there’s no method to get a list of them. Instead, you’ll have to loop through a list of all threads to find the daemons.
Thread[] getAllDaemonThreads( ) { final Thread[] allThreads = getAllThreads( ); final Thread[] daemons = new Thread[allThreads.length]; int nDaemon = 0; for ( Thread thread : allThreads ) if ( thread.isDaemon( ) ) daemons[nDaemon++] = thread; return java.util.Arrays.copyOf( daemons, nDaemon );}
Getting a list of all threads in a specific state
ThegetState()
method onThread
tells you if the thread is runnable or if it is blocked waiting on something. There are six states, defined by theThread.State
enum:
NEW
. The thread has been created, but hasn’t run yet.TERMINATED
. The thread has run to completion, but hasn’t been deleted yet by the JVM.RUNNABLE
. The thread is running.BLOCKED
. The thread is blocked waiting on a lock (such as in asynchronized
block or method).WAITING
. The thread is waiting until another thread callsnotify()
.TIMED_WAITING
. The thread is either waiting or in asleep()
.
During debugging it can be useful to monitor which threads are in which states. To get a list of threads in a specific state, get a list of all threads and extract the ones in the state you want.
Thread[] getAllThreads( final Thread.State state ) { final Thread[] allThreads = getAllThreads( ); final Thread[] found = new Thread[allThreads.length]; int nFound = 0; for ( Thread thread : allThreads ) if ( thread.getState( ) == state ) found[nFound++] = thread; return java.util.Arrays.copyOf( found, nFound );}
Getting a list of all thread IDs
TheThreadMXBean
class already has a method to return an array of IDs for all threads.
long[] getAllThreadIds( ) { final ThreadMXBean thbean = ManagementFactory.getThreadMXBean( ); return thbean.getAllThreadIds( );}
Getting a thread by ID
If you have a thread ID, you’ll have to loop through a list of all threads to find the correspondingThread
object.
Thread getThread( final long id ) { final Thread[] threads = getAllThreads( ); for ( Thread thread : threads ) if ( thread.getId( ) == id ) return thread; return null;}
Getting a thread by name
To find a named thread, again you’ll have to loop through a list of all threads. Since thread names are not guaranteed to be unique, this loop will stop on the first matching thread.
Thread getThread( final String name ) { if ( name == null ) throw new NullPointerException( "Null name" ); final Thread[] threads = getAllThreads( ); for ( Thread thread : threads ) if ( thread.getName( ).equals( name ) ) return thread; return null;}
Getting Thread Info
AThread
object can tell you the thread’s ID, name, priority, and state. But theThread
object doesn’t tell you if the thread owns any locks, and it won’t help you find what a blocked thread is waiting for. For information like this, you need the thread’sThreadInfo
object in thejava.lang.management
package.
There are two forms of theThreadInfo
object. The light-weight form has minimal information and is quick to get. The heavy-weight form has detailed information on locks and a current stack trace. This one is expensive to get but much more useful. Both forms are returned by methods on theThreadMXBean
class returned by theManagementFactory
injava.lang.management
.
You’ll need to keep track of which form ofThreadInfo
you get. Once you’ve got one, they are indistinguishable. But if you ask the light-weight form for lock information, you’ll get an empty response. Only the heavy-weight form will return lock information, if the thread has any locks.
However, there is no method onThread
to get itsThreadInfo
, and no method onThreadInfo
to get itsThread
.
Getting a list of all thread infos
This is a bit more work than it first seems. TheThreadMXBean
object’sgetAllThreadIds()
method returns an array of all thread IDs. Pass that to the bean’sgetThreadInfo()
method to get theThreadInfo
objects for those IDs. However, between one call and the next, threads may have died. When this occurs,getThreadInfo()
adds anull
to the returned array for the corresponding dead thread ID. To get a clean array without these nulls, loop through the returned array and make a new clean array.
There are two important forms of thegetThreadInfo()
method onThreadMXBean
. The first form takes a thread ID array and returns light-weightThreadInfo
objects. The second form adds two boolean arguments to indicate if heavy-weight information on monitors and synchronizers should be included.
Before asking for heavy-weight information, ask the JVM if it supports returning this information. If it doesn’t,getThreadInfo()
will throw an exception.
ThreadInfo[] getAllThreadInfos( ) { final ThreadMXBean thbean = ManagementFactory.getThreadMXBean( ); final long[] ids = thbean.getAllThreadIds( ); ThreadInfo[] infos; if ( !thbean.isObjectMonitorUsageSupported( ) || !thbean.isSynchronizerUsageSupported( ) ) infos = thbean.getThreadInfo( ids ); else infos = thbean.getThreadInfo( ids, true, true ); final ThreadInfo[] notNulls = new ThreadInfo[infos.length]; int nNotNulls = 0; for ( ThreadInfo info : infos ) if ( info != null ) notNulls[nNotNulls++] = info; if ( nNotNulls == infos.length ) return infos; return java.util.Arrays.copyOf( notNulls, nNotNulls );}
Getting thread info by thread ID
Pass the thread ID togetThreadInfo()
on theThreadMXBean
class to get the correspondingThreadInfo
. Ask for heavy-weight lock information if available.
ThreadInfo getThreadInfo( final long id ) { final ThreadMXBean thbean = ManagementFactory.getThreadMXBean( ); if ( !thbean.isObjectMonitorUsageSupported( ) || !thbean.isSynchronizerUsageSupported( ) ) return thbean.getThreadInfo( id ); final ThreadInfo[] infos = thbean.getThreadInfo( new long[] { id }, true, true ); if ( infos.length == 0 ) return null; return infos[0];}
Getting thread info by thread
CallgetId()
on aThread
object, then use thegetThreadInfo()
method above.
ThreadInfo getThreadInfo( final Thread thread ) { if ( thread == null ) throw new NullPointerException( "Null thread" ); return getThreadInfo( thread.getId( ) );}
Getting thread info by thread name
There are two approaches. Get a list of allThreadInfo
objects usinggetAllThreadInfos()
above, then loop through the list to find the one you want. Or, get a list of allThread
objects usinggetAllThreads()
above, loop through that to find the thread you want, then get aThreadInfo
for that thread. Both approaches work, but the second one is faster. ThegetAllThreadInfos()
method is slow because it gets heavy-weight information forallThreadInfo
objects, even though you only need it for one.
ThreadInfo getThreadInfo( final String name ) { if ( name == null ) throw new NullPointerException( "Null name" ); final Thread[] threads = getAllThreads( ); for ( Thread thread : threads ) if ( thread.getName( ).equals( name ) ) return getThreadInfo( thread.getId( ) ); return null;}
Getting a thread by thread info
If you have aThreadInfo
, call it’sgetThreadId()
method, then use the earliergetThread()
method to search for theThread
using the returned ID.
Thread getThread( final ThreadInfo info ) { if ( info == null ) throw new NullPointerException( "Null thread info" ); return getThread( info.getThreadId( ) );}
Getting Monitor and Lock Info
When aThread
enters a synchronized method or block, it gains exclusive access to a resource. That resource is locked and this is recorded in aMonitorInfo
object available through the thread’s heavy-weightThreadInfo
.
However, there is no method to find out whichThread
owns a lock on an object, or whatThread
anotherThread
is blocked on.
Getting the thread that’s locking an object
There are two ways to refer to anObject
in Java. The first is with a standard object reference. The second is with a globally unique "identity hash code". This is not the same as the hash code returned byhashCode()
onObject
. Subclasses can override that method so that its value isn’t globally unique. Instead, you can get an object’s unique identity hash code by callingidentityHashCode()
onSystem
.
The identity hash code is needed here. A heavy-weightThreadInfo
object’sgetLockedMonitors()
method returns a list ofMonitorInfo
objects. Each of these refers to a locked object by its identity hash code. To find the thread holding a lock on an object, loop through all threads and get theThreadInfo
for each one. Since an object can only be locked by one thread at a time, stop on the first identity hash code match.
Thread getLockingThread( final Object object ) { if ( object == null ) throw new NullPointerException( "Null object" ); final long identity = System.identityHashCode( object ); final Thread[] allThreads = getAllThreads( ); ThreadInfo info = null; MonitorInfo[] monitors = null; for ( Thread thread : allThreads ) { info = getThreadInfo( thread.getId( ) ); if ( info == null ) continue; monitors = info.getLockedMonitors( ); for ( MonitorInfo monitor : monitors ) if ( identity == monitor.getIdentityHashCode( ) ) return thread; } return null;}
Getting the object that’s blocking a thread
ThreadInfo
can return aLockInfo
object describing the object a thread is currently blocked by. Call thegetIdentityHashCode()
method onLockInfo
to get the identity hash code of that object.
Now what you need is a method to convert an identity hash code to an object reference. But there is no such method. And it’s unlikely one will ever be added due to serious security and stability issues. Consider that an identity hash code uniquely identifies an object, much like a memory address. A malicious application could explore memory by guessing at hash codes and converting them to object references. This would bypass the JVM’s security checks and enable all sorts of mischief.
Without an identity hash code to object conversion method, it is not possible to find theObject
blocking aThread
.
Getting the thread that owns the object that’s blocking a thread
While you can’t get the object that’s blocking a thread, you can get the thread that owns that object. CallgetLockOwnerId()
on the blocked thread’sThreadInfo
. Then look up that thread by its ID.
Thread getBlockingThread( final Thread thread ) { final ThreadInfo info = getThreadInfo( thread ); if ( info == null ) return null; final long id = info.getLockOwnerId( ); if ( id == -1 ) return null; return getThread( id );}
自己变得跟水晶一般透明,