java thread info

来源:互联网 发布:易顺佳服装软件 编辑:程序博客网 时间:2024/06/06 01:27

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

  1. Introduction
  2. Getting Thread Groups
    1. Getting the root thread group
    2. Getting a list of all thread groups
    3. Getting a thread group by name
  3. Getting Threads
    1. Getting a list of all threads
    2. Getting a list of all threads in a thread group
    3. Getting a list of all threads in high-to-low priority order
    4. Getting a list of all daemon threads
    5. Getting a list of all threads in a specific state
    6. Getting a list of all thread IDs
    7. Getting a thread by ID
    8. Getting a thread by name
  4. Getting Thread Info
    1. Getting a list of all thread infos
    2. Getting thread info by thread ID
    3. Getting thread info by thread
    4. Getting thread info by thread name
    5. Getting a thread by thread info
  5. Getting Monitor and Lock Info
    1. Getting the thread that's locking an object
    2. Getting the object that's blocking a thread
    3. Getting the thread that owns the object that's blocking a thread
  6. Downloads

Introduction

The principal threading classes are Thread and ThreadGroup in java.lang. Java 5 added the ThreadInfo and ThreadMXBean classes in java.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 added java.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 root ThreadGroup. Though threads have names and IDs, there are no methods to search for them by name or ID. Methods on ThreadInfo give you important state and locking information about a thread, but there is no method to get a ThreadInfo for a Thread, or to get the Thread described by a ThreadInfo. 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'll highlight the article's methods.

ThreadUtilities class including all of these methods is available for download. The source is released under the Lesser GPL, version 2.

Getting Thread Groups

Every Thread is in a named ThreadGroup. 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 root ThreadGroup.

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

The enumerate( ) method on a ThreadGroup lists that group's child groups. Pass a true second argument and it will recursively traverse the group and children to fill a given array withThreadGroup objects. Start at the root ThreadGroup 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 to enumerate( ) is too small, the method silently drops some groups. To allocate an array the right size, you could use theactiveGroupCount( ) method on a ThreadGroup but it returns the number of groups in 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 call enumerate( ). 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 the getAllThreadGroups( ) 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

Every Thread 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 a Thread based upon its ID or name. And getting a list of all Thread objects has the same difficulties as getting a list of all ThreadGroup objects did above.

Getting a list of all threads

Another enumerate( ) method on a ThreadGroup lists that group's threads. With a true second argument, it will recursively traverse the group to fill a given array with Thread objects. Start at the root ThreadGroup 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 to enumerate( ) 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, call enumerate( ), check the returned value, and try again if the array filled up. To get a good starting guess, look to thejava.lang.management package. The ManagementFactory class there returns a ThreadMXBean who's getThreadCount( ) 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's enumerate( ) method fills your array with Threadobjects, but you have to allocate the array at the right size or threads might be silently dropped. Call activeCount( ) 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 the getAllThreads( ) 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 on Thread tells you if a thread is a daemon.

The ThreadMXBean class has a getDaemonThreadCount( ) 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

The getState( ) method on Thread tells you if the thread is runnable or if it is blocked waiting on something. There are six states, defined by the Thread.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 a synchronized block or method).
  • WAITING. The thread is waiting until another thread calls notify( ).
  • TIMED_WAITING. The thread is either waiting or in a sleep( ).

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

The ThreadMXBean 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 corresponding Thread 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

Thread object can tell you the thread's ID, name, priority, and state. But the Thread 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's ThreadInfo object in the java.lang.management package.

There are two forms of the ThreadInfo 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 the ThreadMXBean class returned by the ManagementFactory injava.lang.management.

You'll need to keep track of which form of ThreadInfo 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 on Thread to get its ThreadInfo, and no method on ThreadInfo to get its Thread.

Getting a list of all thread infos

This is a bit more work than it first seems. The ThreadMXBean object's getAllThreadIds( ) method returns an array of all thread IDs. Pass that to the bean's getThreadInfo( ) method to get the ThreadInfo objects for those IDs. However, between one call and the next, threads may have died. When this occurs, getThreadInfo( ) adds a null 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 the getThreadInfo( ) method on ThreadMXBean. The first form takes a thread ID array and returns light-weight ThreadInfo 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 to getThreadInfo( ) on the ThreadMXBean class to get the corresponding ThreadInfo. 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

Call getId( ) on a Thread object, then use the getThreadInfo( ) 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 all ThreadInfo objects using getAllThreadInfos( ) above, then loop through the list to find the one you want. Or, get a list of all Thread objects using getAllThreads( ) above, loop through that to find the thread you want, then get a ThreadInfo for that thread. Both approaches work, but the second one is faster. ThegetAllThreadInfos( ) method is slow because it gets heavy-weight information for all ThreadInfo 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 a ThreadInfo, call it's getThreadId( ) method, then use the earlier getThread( ) method to search for the Thread 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 a Thread enters a synchronized method or block, it gains exclusive access to a resource. That resource is locked and this is recorded in a MonitorInfo object available through the thread's heavy-weight ThreadInfo.

However, there is no method to find out which Thread owns a lock on an object, or what Thread another Thread is blocked on.

Getting the thread that's locking an object

There are two ways to refer to an Object 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 by hashCode( ) on Object. 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 calling identityHashCode( ) on System.

The identity hash code is needed here. A heavy-weight ThreadInfo object's getLockedMonitors( ) method returns a list of MonitorInfo 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 the ThreadInfo 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 a LockInfo object describing the object a thread is currently blocked by. Call the getIdentityHashCode( ) method on LockInfo 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 the Object blocking a Thread.

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. Call getLockOwnerId( ) on the blocked thread's ThreadInfo. 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 );}
0 0
原创粉丝点击