1. /**

  2. * Copyright 2008, David Robert Nadeau, NadeauSoftware.com

  3. *

  4. * This file is free software: you can redistribute it and/or modify

  5. * it under the terms of the GNU Lesser General Public License as

  6. * published by the Free Software Foundation, either version 2 of

  7. * the License, or (at your option) any later version.

  8. *

  9. * This file is distributed in the hope that it will be useful,

  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of

  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

  12. * GNU Lesser General Public License for more details.

  13. *

  14. * You should have received a copy of the GNU Lesser General Public

  15. * License along with this file.  If not, see

  16. * <a href="http://www.gnu.org/licenses">GNU Licenses</a>.

  17. */

  18. import java.lang.management.*;  

  19. /**

  20. * The ThreadUtilities class provides a collection of static methods

  21. * for listing and finding Thread, ThreadGroup, and ThreadInfo objects.

  22. * <p>

  23. * Please see the accompanying article:

  24. * <a href="http://nadeausoftware.com/articles/2008/04/java_tip_how_list_and_find_threads_and_thread_groups">Java tip:  How to list and find threads and thread groups</a>

  25. *

  26. * @author  <a href="http://NadeauSoftware.com/">David R. Nadeau</a>

  27. */

  28. publicfinalclass ThreadUtilities  

  29. {  

  30. // Dummy constructor

  31. /**

  32.     * The ThreadUtilities class cannot be instanced.

  33.     */

  34. private ThreadUtilities( )  

  35.    {  

  36.    }  

  37. // Thread groups

  38. /**

  39.     * The root thread group saved on the first search for it.

  40.     * The root group doesn't change for the life of the JVM,

  41.     * so once found there is no need to find it again.

  42.     */

  43. privatestatic ThreadGroup rootThreadGroup = null;  

  44. /**

  45.     * Get the root thread group in the thread group tree.

  46.     * Since there is always a root thread group, this

  47.     * method never returns null.

  48.     *

  49.     * @return      the root thread group

  50.     */

  51. publicstatic ThreadGroup getRootThreadGroup( )  

  52.    {  

  53. if ( rootThreadGroup != null )  

  54. return rootThreadGroup;  

  55.        ThreadGroup tg = Thread.currentThread( ).getThreadGroup( );  

  56.        ThreadGroup ptg;  

  57. while ( (ptg = tg.getParent( )) != null )  

  58.            tg = ptg;  

  59.        rootThreadGroup = tg;  

  60. return tg;  

  61.    }  

  62. /**

  63.     * Get a list of all thread groups.  Since there is

  64.     * always at least one thread group (the root), this

  65.     * method never returns a null or empty array.

  66.     *

  67.     * @return      an array of thread groups

  68.     */

  69. publicstatic ThreadGroup[] getAllThreadGroups( )  

  70.    {  

  71. final ThreadGroup root = getRootThreadGroup( );  

  72. int nAlloc = root.activeGroupCount( );  

  73. int n = 0;  

  74.        ThreadGroup[] groups = null;  

  75. do

  76.        {  

  77.            nAlloc *= 2;  

  78.            groups = new ThreadGroup[ nAlloc ];  

  79.            n = root.enumerate( groups, true );  

  80.        } while ( n == nAlloc );  

  81.        ThreadGroup[] allGroups = new ThreadGroup[n+1];  

  82.        allGroups[0] = root;  

  83.        System.arraycopy( groups, 0, allGroups, 1, n );  

  84. return allGroups;  

  85.    }  

  86. /**

  87.     * Get the thread group with the given name.  A null is

  88.     * returned if no such group is found.  If more than one

  89.     * group has the same name, the first one found is returned.

  90.     *

  91.     * @param   name    the thread group name to search for

  92.     * @return      the thread group, or null if not found

  93.     * @throws  NullPointerException

  94.     *          if the name is null

  95.     */

  96. publicstatic ThreadGroup getThreadGroup( final String name )  

  97.    {  

  98. if ( name == null )  

  99. thrownew NullPointerException( "Null name" );  

  100. final ThreadGroup[] groups = getAllThreadGroups( );  

  101. for ( ThreadGroup group : groups )  

  102. if ( group.getName( ).equals( name ) )  

  103. return group;  

  104. returnnull;  

  105.    }  

  106. // Threads

  107. /**

  108.     * Get a list of all threads.  Since there is always at

  109.     * least one thread, this method never returns null or

  110.     * an empty array.

  111.     *

  112.     * @return      an array of threads

  113.     */

  114. publicstatic Thread[] getAllThreads( )  

  115.    {  

  116. final ThreadGroup root = getRootThreadGroup( );  

  117. final ThreadMXBean thbean =  

  118.            ManagementFactory.getThreadMXBean( );  

  119. int nAlloc = thbean.getThreadCount( );  

  120. int n = 0;  

  121.        Thread[] threads = null;  

  122. do

  123.        {  

  124.            nAlloc *= 2;  

  125.            threads = new Thread[ nAlloc ];  

  126.            n = root.enumerate( threads, true );  

  127.        } while ( n == nAlloc );  

  128. return java.util.Arrays.copyOf( threads, n );  

  129.    }  

  130. /**

  131.     * Get a list of all threads in a thread group.  An empty

  132.     * array is returned if there are no threads in the group.

  133.     *

  134.     * @param   group   the thread group to list

  135.     * @return      an array of threads

  136.     * @throws  NullPointerException

  137.     *          if the group is null

  138.     */

  139. publicstatic Thread[] getGroupThreads( final ThreadGroup group )  

  140.    {  

  141. if ( group == null )  

  142. thrownew NullPointerException( "Null group" );  

  143. int nAlloc = group.activeCount( );  

  144. int n = 0;  

  145.        Thread[] threads = null;  

  146. do

  147.        {  

  148.            nAlloc *= 2;  

  149.            threads = new Thread[ nAlloc ];  

  150.            n = group.enumerate( threads, false );  

  151.        } while ( n == nAlloc );  

  152. return java.util.Arrays.copyOf( threads, n );  

  153.    }  

  154. /**

  155.     * Get a list of all threads in a named thread group.

  156.     * A null is returned if the group cannot be found.

  157.     * An empty array is returned if there are no threads in

  158.     * the group.

  159.     *

  160.     * @param   name    the name of the thread group

  161.     * @return      an array of threads, or null if the

  162.     *          group is not found

  163.     * @throws  NullPointerException

  164.     *          if the name is null

  165.     */

  166. publicstatic Thread[] getGroupThreads( final String name )  

  167.    {  

  168. final ThreadGroup group = getThreadGroup( name );  

  169. if ( group == null )  

  170. returnnull;  

  171. return getGroupThreads( group );  

  172.    }  

  173. /**

  174.     * Get a list of all threads, sorted from highest to

  175.     * lowest priority.  Since there is always at least one

  176.     * thread, this method never returns null or an empty

  177.     * array.  Since threads may change their priority during

  178.     * this method's sort, the returned thread list may not be

  179.     * correct by the time it is returned.

  180.     *

  181.     * @return      an array of threads

  182.     */

  183. publicstatic Thread[] getAllThreadsPrioritized( )  

  184.    {  

  185. final Thread[] allThreads = getAllThreads( );  

  186.        java.util.Arrays.sort( allThreads,  

  187. new java.util.Comparator<Thread>( )  

  188.        {  

  189. publicint compare( final Thread t1, final Thread t2 )  

  190.            { return t2.getPriority( ) - t1.getPriority( ); }  

  191.        } );  

  192. return allThreads;  

  193.    }  

  194. /**

  195.     * Get a list of all daemon threads.  An empty array is

  196.     * returned if there are no daemon threads.

  197.     *

  198.     * @return      an array of daemon threads

  199.     */

  200. publicstatic Thread[] getAllDaemonThreads( )  

  201.    {  

  202. final Thread[] allThreads = getAllThreads( );  

  203. final Thread[] daemons = new Thread[allThreads.length];  

  204. int nDaemon = 0;  

  205. for ( Thread thread : allThreads )  

  206. if ( thread.isDaemon( ) )  

  207.                daemons[nDaemon++] = thread;  

  208. return java.util.Arrays.copyOf( daemons, nDaemon );  

  209.    }  

  210. /**

  211.     * Get a list of all threads with a given thread state.

  212.     * Thread states are defined in the Thread.State enum for

  213.     * the Thread class.  Principal thread states include

  214.     * RUNNABLE, WAITING, TIMED_WAITING, and BLOCKED.  An

  215.     * empty array is returned if there are no threads in

  216.     * the chosen state.

  217.     *

  218.     * @param   state   the state to look for

  219.     * @return      an array of threads in that state

  220.     */

  221. publicstatic Thread[] getAllThreads( final Thread.State state )  

  222.    {  

  223. final Thread[] allThreads = getAllThreads( );  

  224. final Thread[] found = new Thread[allThreads.length];  

  225. int nFound = 0;  

  226. for ( Thread thread : allThreads )  

  227. if ( thread.getState( ) == state )  

  228.                found[nFound++] = thread;  

  229. return java.util.Arrays.copyOf( found, nFound );  

  230.    }  

  231. /**

  232.     * Get the thread with the given name.  A null is returned

  233.     * if no such thread is found.  If more than one thread has

  234.     * the same name, the first one found is returned.

  235.     *

  236.     * @param   name    the thread name to search for

  237.     * @return      the thread, or null if not found

  238.     * @throws  NullPointerException

  239.     *          if the name is null

  240.     */

  241. publicstatic Thread getThread( final String name )  

  242.    {  

  243. if ( name == null )  

  244. thrownew NullPointerException( "Null name" );  

  245. final Thread[] threads = getAllThreads( );  

  246. for ( Thread thread : threads )  

  247. if ( thread.getName( ).equals( name ) )  

  248. return thread;  

  249. returnnull;  

  250.    }  

  251. /**

  252.     * Get the thread with the given ID.  A null is returned

  253.     * if no such thread is found.

  254.     *

  255.     * @param   id  the thread ID to search for

  256.     * @return      the thread, or null if not found

  257.     */

  258. publicstatic Thread getThread( finallong id )  

  259.    {  

  260. final Thread[] threads = getAllThreads( );  

  261. for ( Thread thread : threads )  

  262. if ( thread.getId( ) == id )  

  263. return thread;  

  264. returnnull;  

  265.    }  

  266. /**

  267.     * Get the thread for the given thread info.  A null

  268.     * is returned if the thread cannot be found.

  269.     *

  270.     * @param   info    the thread info to search for

  271.     * @return      the thread, or null if not found

  272.     * @throws  NullPointerException

  273.     *          if info is null

  274.     */

  275. publicstatic Thread getThread( final ThreadInfo info )  

  276.    {  

  277. if ( info == null )  

  278. thrownew NullPointerException( "Null info" );  

  279. return getThread( info.getThreadId( ) );  

  280.    }  

  281. // ThreadInfo

  282. /**

  283.     * Get a list of all thread info objects.  Since there is

  284.     * always at least one thread running, there is always at

  285.     * least one thread info object.  This method never returns

  286.     * a null or empty array.

  287.     *

  288.     * @return      an array of thread infos

  289.     */

  290. publicstatic ThreadInfo[] getAllThreadInfos( )  

  291.    {  

  292. final ThreadMXBean thbean =  

  293.            ManagementFactory.getThreadMXBean( );  

  294. finallong[] ids = thbean.getAllThreadIds( );  

  295. // Get thread info with lock info, when available.

  296.        ThreadInfo[] infos;  

  297. if ( !thbean.isObjectMonitorUsageSupported( ) ||  

  298.            !thbean.isSynchronizerUsageSupported( ) )  

  299.            infos = thbean.getThreadInfo( ids );  

  300. else

  301.            infos = thbean.getThreadInfo( ids, true, true );  

  302. // Clean nulls from array if threads have died.

  303. final ThreadInfo[] notNulls = new ThreadInfo[infos.length];  

  304. int nNotNulls = 0;  

  305. for ( ThreadInfo info : infos )  

  306. if ( info != null )  

  307.                notNulls[nNotNulls++] = info;  

  308. if ( nNotNulls == infos.length )  

  309. return infos;   // Original had no nulls

  310. return java.util.Arrays.copyOf( notNulls, nNotNulls );  

  311.    }  

  312. /**

  313.     * Get the thread info for the thread with the given name.

  314.     * A null is returned if no such thread info is found.

  315.     * If more than one thread has the same name, the thread

  316.     * info for the first one found is returned.

  317.     *

  318.     * @param   name    the thread name to search for

  319.     * @return      the thread info, or null if not found

  320.     * @throws  NullPointerException

  321.     *          if the name is null

  322.     */

  323. publicstatic ThreadInfo getThreadInfo( final String name )  

  324.    {  

  325. if ( name == null )  

  326. thrownew NullPointerException( "Null name" );  

  327. final Thread[] threads = getAllThreads( );  

  328. for ( Thread thread : threads )  

  329. if ( thread.getName( ).equals( name ) )  

  330. return getThreadInfo( thread.getId( ) );  

  331. returnnull;  

  332.    }  

  333. /**

  334.     * Get the thread info for the thread with the given ID.

  335.     * A null is returned if no such thread info is found.

  336.     *

  337.     * @param   id  the thread ID to search for

  338.     * @return      the thread info, or null if not found

  339.     * @throws  IllegalArgumentException

  340.     *          if id <= 0

  341.     */

  342. publicstatic ThreadInfo getThreadInfo( finallong id )  

  343.    {  

  344. final ThreadMXBean thbean =  

  345.            ManagementFactory.getThreadMXBean( );  

  346. // Get thread info with lock info, when available.

  347. if ( !thbean.isObjectMonitorUsageSupported( ) ||  

  348.            !thbean.isSynchronizerUsageSupported( ) )  

  349. return thbean.getThreadInfo( id );  

  350. final ThreadInfo[] infos = thbean.getThreadInfo(  

  351. newlong[] { id }, true, true );  

  352. if ( infos.length == 0 )  

  353. returnnull;  

  354. return infos[0];  

  355.    }  

  356. /**

  357.     * Get the thread info for the given thread.  A null is

  358.     * returned if the thread info cannot be found.

  359.     *

  360.     * @param   thread  the thread to search for

  361.     * @return      the thread info, or null if not found

  362.     * @throws  NullPointerException

  363.     *          if thread is null

  364.     */

  365. publicstatic ThreadInfo getThreadInfo( final Thread thread )  

  366.    {  

  367. if ( thread == null )  

  368. thrownew NullPointerException( "Null thread" );  

  369. return getThreadInfo( thread.getId( ) );  

  370.    }  

  371. // MonitorInfo and LockInfo

  372. /**

  373.     * Get the thread holding a lock on the given object.

  374.     * A null is returned if there is no such thread.

  375.     *

  376.     * @param   object      the object to look for a lock on

  377.     * @return          the thread holding a lock, or

  378.     *              null if there is none

  379.     * @throws  NullPointerException

  380.     *              if the object is null

  381.     */

  382. publicstatic Thread getLockingThread( final Object object )  

  383.    {  

  384. if ( object == null )  

  385. thrownew NullPointerException( "Null object" );  

  386. finallong identity = System.identityHashCode( object );  

  387. final Thread[] allThreads = getAllThreads( );  

  388.        ThreadInfo info = null;  

  389.        MonitorInfo[] monitors = null;  

  390. for ( Thread thread : allThreads )  

  391.        {  

  392.            info = getThreadInfo( thread.getId( ) );  

  393. if ( info == null )  

  394. continue;  

  395.            monitors = info.getLockedMonitors( );  

  396. for ( MonitorInfo monitor : monitors )  

  397. if ( identity == monitor.getIdentityHashCode( ) )  

  398. return thread;  

  399.        }  

  400. returnnull;  

  401.    }  

  402. /**

  403.     * Get the thread who's lock is blocking the given thread.

  404.     * A null is returned if there is no such thread.

  405.     *

  406.     * @param   blockedThread   the blocked thread

  407.     * @return          the blocking thread, or null if

  408.     *              there is none

  409.     * @throws  NullPointerException

  410.     *              if the blocked thread is null

  411.     */

  412. publicstatic Thread getBlockingThread( final Thread blockedThread )  

  413.    {  

  414. final ThreadInfo info = getThreadInfo( blockedThread );  

  415. if ( info == null )  

  416. returnnull;  

  417. finallong id = info.getLockOwnerId( );  

  418. if ( id == -1 )  

  419. returnnull;  

  420. return getThread( id );  

  421.    }  

  422. }