java thread info

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 areThreadandThreadGroupinjava.lang. Java 5 added theThreadInfoandThreadMXBeanclasses injava.lang.managementto 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.concurrentclasses 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 onThreadInfogive you important state and locking information about a thread, but there is no method to get aThreadInfofor aThread,or to get theThreaddescribed 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.

AThreadUtilitiesclass including all of these methods is available fordownload. The source is released under the Lesser GPL, version 2.

Getting Thread Groups

EveryThreadis 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 aThreadGrouplists that group’s child groups. Pass atruesecond argument and it will recursively traverse the group and children to fill a given array withThreadGroupobjects. Start at the rootThreadGroupand 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 aThreadGroupbut 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

EveryThreadhas 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 aThreadbased upon its ID or name. And getting a list of allThreadobjects has the same difficulties as getting a list of allThreadGroupobjects did above.

Getting a list of all threads

Anotherenumerate()method on aThreadGrouplists that group’s threads. With atruesecond argument, it will recursively traverse the group to fill a given array withThreadobjects. Start at the rootThreadGroupand 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.managementpackage. TheManagementFactoryclass there returns aThreadMXBeanwho’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 withThreadobjects, 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 onThreadtells you if a thread is a daemon.

TheThreadMXBeanclass 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 onThreadtells you if the thread is runnable or if it is blocked waiting on something. There are six states, defined by theThread.Stateenum:

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 asynchronizedblock 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

TheThreadMXBeanclass 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 correspondingThreadobject.

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

AThreadobject can tell you the thread’s ID, name, priority, and state. But theThreadobject 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’sThreadInfoobject in thejava.lang.managementpackage.

There are two forms of theThreadInfoobject. 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 theThreadMXBeanclass returned by theManagementFactoryinjava.lang.management.

You’ll need to keep track of which form ofThreadInfoyou 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 onThreadto get itsThreadInfo, and no method onThreadInfoto get itsThread.

Getting a list of all thread infos

This is a bit more work than it first seems. TheThreadMXBeanobject’sgetAllThreadIds()method returns an array of all thread IDs. Pass that to the bean’sgetThreadInfo()method to get theThreadInfoobjects for those IDs. However, between one call and the next, threads may have died. When this occurs,getThreadInfo()adds anullto 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-weightThreadInfoobjects. 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 theThreadMXBeanclass 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 aThreadobject, 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 allThreadInfoobjects usinggetAllThreadInfos()above, then loop through the list to find the one you want. Or, get a list of allThreadobjects usinggetAllThreads()above, loop through that to find the thread you want, then get aThreadInfofor that thread. Both approaches work, but the second one is faster. ThegetAllThreadInfos()method is slow because it gets heavy-weight information forallThreadInfoobjects, 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 theThreadusing 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 aThreadenters a synchronized method or block, it gains exclusive access to a resource. That resource is locked and this is recorded in aMonitorInfoobject available through the thread’s heavy-weightThreadInfo.

However, there is no method to find out whichThreadowns a lock on an object, or whatThreadanotherThreadis blocked on.

Getting the thread that’s locking an object

There are two ways to refer to anObjectin 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-weightThreadInfoobject’sgetLockedMonitors()method returns a list ofMonitorInfoobjects. 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 theThreadInfofor 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

ThreadInfocan return aLockInfoobject describing the object a thread is currently blocked by. Call thegetIdentityHashCode()method onLockInfoto 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 theObjectblocking 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 );}

自己变得跟水晶一般透明,

java thread info

相关文章:

你感兴趣的文章:

标签云: