    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.lang3;
    018
    019import java.util.ArrayList;
    020import java.util.Collection;
    021import java.util.Collections;
    022import java.util.List;
    023
    024/**
    025 * <p>
    026 * Helpers for {@code java.lang.Thread} and {@code java.lang.ThreadGroup}.
    027 * </p>
    028 * <p>
    029 * #ThreadSafe#
    030 * </p>
    031 *
    032 * @see java.lang.Thread
    033 * @see java.lang.ThreadGroup
    034 * @since 3.5
    035 */
    036public class ThreadUtils {
    037
    038    /**
    039     * Return the active thread with the specified id if it belongs to the specified thread group.
    040     *
    041     * @param threadId The thread id
    042     * @param threadGroup The thread group
    043     * @return The thread which belongs to a specified thread group and the thread's id match the specified id.
    044     * {@code null} is returned if no such thread exists
    045     * @throws IllegalArgumentException if the specified id is zero or negative or the group is null
    046     * @throws  SecurityException
    047     *          if the current thread cannot access the system thread group
    048     *
    049     * @throws  SecurityException  if the current thread cannot modify
    050     *          thread groups from this thread's thread group up to the system thread group
    051     */
    052    public static Thread findThreadById(final long threadId, final ThreadGroup threadGroup) {
    053        Validate.isTrue(threadGroup != null, "The thread group must not be null");
    054        final Thread thread = findThreadById(threadId);
    055        if(thread != null && threadGroup.equals(thread.getThreadGroup())) {
    056            return thread;
    057        }
    058        return null;
    059    }
    060
    061    /**
    062     * Return the active thread with the specified id if it belongs to a thread group with the specified group name.
    063     *
    064     * @param threadId The thread id
    065     * @param threadGroupName The thread group name
    066     * @return The threads which belongs to a thread group with the specified group name and the thread's id match the specified id.
    067     * {@code null} is returned if no such thread exists
    068     * @throws IllegalArgumentException if the specified id is zero or negative or the group name is null
    069     * @throws  SecurityException
    070     *          if the current thread cannot access the system thread group
    071     *
    072     * @throws  SecurityException  if the current thread cannot modify
    073     *          thread groups from this thread's thread group up to the system thread group
    074     */
    075    public static Thread findThreadById(final long threadId, final String threadGroupName) {
    076        Validate.isTrue(threadGroupName != null, "The thread group name must not be null");
    077        final Thread thread = findThreadById(threadId);
    078        if(thread != null && thread.getThreadGroup() != null && thread.getThreadGroup().getName().equals(threadGroupName)) {
    079            return thread;
    080        }
    081        return null;
    082    }
    083
    084    /**
    085     * Return active threads with the specified name if they belong to a specified thread group.
    086     *
    087     * @param threadName The thread name
    088     * @param threadGroup The thread group
    089     * @return The threads which belongs to a thread group and the thread's name match the specified name,
    090     * An empty collection is returned if no such thread exists. The collection returned is always unmodifiable.
    091     * @throws IllegalArgumentException if the specified thread name or group is null
    092     * @throws  SecurityException
    093     *          if the current thread cannot access the system thread group
    094     *
    095     * @throws  SecurityException  if the current thread cannot modify
    096     *          thread groups from this thread's thread group up to the system thread group
    097     */
    098    public static Collection<Thread> findThreadsByName(final String threadName, final ThreadGroup threadGroup) {
    099        return findThreads(threadGroup, false, new NamePredicate(threadName));
    100    }
    101
    102    /**
    103     * Return active threads with the specified name if they belong to a thread group with the specified group name.
    104     *
    105     * @param threadName The thread name
    106     * @param threadGroupName The thread group name
    107     * @return The threads which belongs to a thread group with the specified group name and the thread's name match the specified name,
    108     * An empty collection is returned if no such thread exists. The collection returned is always unmodifiable.
    109     * @throws IllegalArgumentException if the specified thread name or group name is null
    110     * @throws  SecurityException
    111     *          if the current thread cannot access the system thread group
    112     *
    113     * @throws  SecurityException  if the current thread cannot modify
    114     *          thread groups from this thread's thread group up to the system thread group
    115     */
    116    public static Collection<Thread> findThreadsByName(final String threadName, final String threadGroupName) {
    117        Validate.isTrue(threadName != null, "The thread name must not be null");
    118        Validate.isTrue(threadGroupName != null, "The thread group name must not be null");
    119
    120        final Collection<ThreadGroup> threadGroups = findThreadGroups(new NamePredicate(threadGroupName));
    121
    122        if(threadGroups.isEmpty()) {
    123            return Collections.emptyList();
    124        }
    125
    126        final Collection<Thread> result = new ArrayList<>();
    127        final NamePredicate threadNamePredicate = new NamePredicate(threadName);
    128        for(final ThreadGroup group : threadGroups) {
    129            result.addAll(findThreads(group, false, threadNamePredicate));
    130        }
    131        return Collections.unmodifiableCollection(result);
    132    }
    133
    134    /**
    135     * Return active thread groups with the specified group name.
    136     *
    137     * @param threadGroupName The thread group name
    138     * @return the thread groups with the specified group name or an empty collection if no such thread group exists. The collection returned is always unmodifiable.
    139     * @throws IllegalArgumentException if group name is null
    140     * @throws  SecurityException
    141     *          if the current thread cannot access the system thread group
    142     *
    143     * @throws  SecurityException  if the current thread cannot modify
    144     *          thread groups from this thread's thread group up to the system thread group
    145     */
    146    public static Collection<ThreadGroup> findThreadGroupsByName(final String threadGroupName) {
    147        return findThreadGroups(new NamePredicate(threadGroupName));
    148    }
    149
    150    /**
    151     * Return all active thread groups excluding the system thread group (A thread group is active if it has been not destroyed).
    152     *
    153     * @return all thread groups excluding the system thread group. The collection returned is always unmodifiable.
    154     * @throws  SecurityException
    155     *          if the current thread cannot access the system thread group
    156     *
    157     * @throws  SecurityException  if the current thread cannot modify
    158     *          thread groups from this thread's thread group up to the system thread group
    159     */
    160    public static Collection<ThreadGroup> getAllThreadGroups() {
    161        return findThreadGroups(ALWAYS_TRUE_PREDICATE);
    162    }
    163
    164    /**
    165     * Return the system thread group (sometimes also referred as "root thread group").
    166     *
    167     * @return the system thread group
    168     * @throws  SecurityException  if the current thread cannot modify
    169     *          thread groups from this thread's thread group up to the system thread group
    170     */
    171    public static ThreadGroup getSystemThreadGroup() {
    172        ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
    173        while(threadGroup.getParent() != null) {
    174            threadGroup = threadGroup.getParent();
    175        }
    176        return threadGroup;
    177    }
    178
    179    /**
    180     * Return all active threads (A thread is active if it has been started and has not yet died).
    181     *
    182     * @return all active threads. The collection returned is always unmodifiable.
    183     * @throws  SecurityException
    184     *          if the current thread cannot access the system thread group
    185     *
    186     * @throws  SecurityException  if the current thread cannot modify
    187     *          thread groups from this thread's thread group up to the system thread group
    188     */
    189    public static Collection<Thread> getAllThreads() {
    190        return findThreads(ALWAYS_TRUE_PREDICATE);
    191    }
    192
    193    /**
    194     * Return active threads with the specified name.
    195     *
    196     * @param threadName The thread name
    197     * @return The threads with the specified name or an empty collection if no such thread exists. The collection returned is always unmodifiable.
    198     * @throws IllegalArgumentException if the specified name is null
    199     * @throws  SecurityException
    200     *          if the current thread cannot access the system thread group
    201     *
    202     * @throws  SecurityException  if the current thread cannot modify
    203     *          thread groups from this thread's thread group up to the system thread group
    204     */
    205    public static Collection<Thread> findThreadsByName(final String threadName) {
    206        return findThreads(new NamePredicate(threadName));
    207    }
    208
    209    /**
    210     * Return the active thread with the specified id.
    211     *
    212     * @param threadId The thread id
    213     * @return The thread with the specified id or {@code null} if no such thread exists
    214     * @throws IllegalArgumentException if the specified id is zero or negative
    215     * @throws  SecurityException
    216     *          if the current thread cannot access the system thread group
    217     *
    218     * @throws  SecurityException  if the current thread cannot modify
    219     *          thread groups from this thread's thread group up to the system thread group
    220     */
    221    public static Thread findThreadById(final long threadId) {
    222        final Collection<Thread> result = findThreads(new ThreadIdPredicate(threadId));
    223        return result.isEmpty() ? null : result.iterator().next();
    224    }
    225
    226    /**
    227     * <p>
    228     * ThreadUtils instances should NOT be constructed in standard programming. Instead, the class should be used as
    229     * {@code ThreadUtils.getAllThreads()}
    230     * </p>
    231     * <p>
    232     * This constructor is public to permit tools that require a JavaBean instance to operate.
    233     * </p>
    234     */
    235    public ThreadUtils() {
    236        super();
    237    }
    238
    239    /**
    240     * A predicate for selecting threads.
    241     */
    242    //if java minimal version for lang becomes 1.8 extend this interface from java.util.function.Predicate
    243    public interface ThreadPredicate /*extends java.util.function.Predicate<Thread>*/{
    244
    245        /**
    246         * Evaluates this predicate on the given thread.
    247         * @param thread the thread
    248         * @return {@code true} if the thread matches the predicate, otherwise {@code false}
    249         */
    250        boolean test(Thread thread);
    251    }
    252
    253    /**
    254     * A predicate for selecting threadgroups.
    255     */
    256    //if java minimal version for lang becomes 1.8 extend this interface from java.util.function.Predicate
    257    public interface ThreadGroupPredicate /*extends java.util.function.Predicate<ThreadGroup>*/{
    258
    259        /**
    260         * Evaluates this predicate on the given threadgroup.
    261         * @param threadGroup the threadgroup
    262         * @return {@code true} if the threadGroup matches the predicate, otherwise {@code false}
    263         */
    264        boolean test(ThreadGroup threadGroup);
    265    }
    266
    267    /**
    268     * Predicate which always returns true.
    269     */
    270    public static final AlwaysTruePredicate ALWAYS_TRUE_PREDICATE = new AlwaysTruePredicate();
    271
    272    /**
    273     * A predicate implementation which always returns true.
    274     */
    275    private static final class AlwaysTruePredicate implements ThreadPredicate, ThreadGroupPredicate{
    276
    277        private AlwaysTruePredicate() {
    278        }
    279
    280        @Override
    281        public boolean test(final ThreadGroup threadGroup) {
    282            return true;
    283        }
    284
    285        @Override
    286        public boolean test(final Thread thread) {
    287            return true;
    288        }
    289    }
    290
    291    /**
    292     * A predicate implementation which matches a thread or threadgroup name.
    293     */
    294    public static class NamePredicate implements ThreadPredicate, ThreadGroupPredicate {
    295
    296        private final String name;
    297
    298        /**
    299         * Predicate constructor
    300         *
    301         * @param name thread or threadgroup name
    302         * @throws IllegalArgumentException if the name is {@code null}
    303         */
    304        public NamePredicate(final String name) {
    305            super();
    306            Validate.isTrue(name != null, "The name must not be null");
    307            this.name = name;
    308        }
    309
    310        @Override
    311        public boolean test(final ThreadGroup threadGroup) {
    312            return threadGroup != null && threadGroup.getName().equals(name);
    313        }
    314
    315        @Override
    316        public boolean test(final Thread thread) {
    317            return thread != null && thread.getName().equals(name);
    318        }
    319    }
    320
    321    /**
    322     * A predicate implementation which matches a thread id.
    323     */
    324    public static class ThreadIdPredicate implements ThreadPredicate {
    325
    326        private final long threadId;
    327
    328        /**
    329         * Predicate constructor
    330         *
    331         * @param threadId the threadId to match
    332         * @throws IllegalArgumentException if the threadId is zero or negative
    333         */
    334        public ThreadIdPredicate(final long threadId) {
    335            super();
    336            if (threadId <= 0) {
    337                throw new IllegalArgumentException("The thread id must be greater than zero");
    338            }
    339            this.threadId = threadId;
    340        }
    341
    342        @Override
    343        public boolean test(final Thread thread) {
    344            return thread != null && thread.getId() == threadId;
    345        }
    346    }
    347
    348    /**
    349     * Select all active threads which match the given predicate.
    350     *
    351     * @param predicate the predicate
    352     * @return An unmodifiable {@code Collection} of active threads matching the given predicate
    353     *
    354     * @throws IllegalArgumentException if the predicate is null
    355     * @throws  SecurityException
    356     *          if the current thread cannot access the system thread group
    357     * @throws  SecurityException  if the current thread cannot modify
    358     *          thread groups from this thread's thread group up to the system thread group
    359     */
    360    public static Collection<Thread> findThreads(final ThreadPredicate predicate){
    361        return findThreads(getSystemThreadGroup(), true, predicate);
    362    }
    363
    364    /**
    365     * Select all active threadgroups which match the given predicate.
    366     *
    367     * @param predicate the predicate
    368     * @return An unmodifiable {@code Collection} of active threadgroups matching the given predicate
    369     * @throws IllegalArgumentException if the predicate is null
    370     * @throws  SecurityException
    371     *          if the current thread cannot access the system thread group
    372     * @throws  SecurityException  if the current thread cannot modify
    373     *          thread groups from this thread's thread group up to the system thread group
    374     */
    375    public static Collection<ThreadGroup> findThreadGroups(final ThreadGroupPredicate predicate){
    376        return findThreadGroups(getSystemThreadGroup(), true, predicate);
    377    }
    378
    379    /**
    380     * Select all active threads which match the given predicate and which belongs to the given thread group (or one of its subgroups).
    381     *
    382     * @param group the thread group
    383     * @param recurse if {@code true} then evaluate the predicate recursively on all threads in all subgroups of the given group
    384     * @param predicate the predicate
    385     * @return An unmodifiable {@code Collection} of active threads which match the given predicate and which belongs to the given thread group
    386     * @throws IllegalArgumentException if the given group or predicate is null
    387     * @throws  SecurityException  if the current thread cannot modify
    388     *          thread groups from this thread's thread group up to the system thread group
    389     */
    390    public static Collection<Thread> findThreads(final ThreadGroup group, final boolean recurse, final ThreadPredicate predicate) {
    391        Validate.isTrue(group != null, "The group must not be null");
    392        Validate.isTrue(predicate != null, "The predicate must not be null");
    393
    394        int count = group.activeCount();
    395        Thread[] threads;
    396        do {
    397            threads = new Thread[count + (count / 2) + 1]; //slightly grow the array size
    398            count = group.enumerate(threads, recurse);
    399            //return value of enumerate() must be strictly less than the array size according to javadoc
    400        } while (count >= threads.length);
    401
    402        final List<Thread> result = new ArrayList<>(count);
    403        for (int i = 0; i < count; ++i) {
    404            if (predicate.test(threads[i])) {
    405                result.add(threads[i]);
    406            }
    407        }
    408        return Collections.unmodifiableCollection(result);
    409    }
    410
    411    /**
    412     * Select all active threadgroups which match the given predicate and which is a subgroup of the given thread group (or one of its subgroups).
    413     *
    414     * @param group the thread group
    415     * @param recurse if {@code true} then evaluate the predicate recursively on all threadgroups in all subgroups of the given group
    416     * @param predicate the predicate
    417     * @return An unmodifiable {@code Collection} of active threadgroups which match the given predicate and which is a subgroup of the given thread group
    418     * @throws IllegalArgumentException if the given group or predicate is null
    419     * @throws  SecurityException  if the current thread cannot modify
    420     *          thread groups from this thread's thread group up to the system thread group
    421     */
    422    public static Collection<ThreadGroup> findThreadGroups(final ThreadGroup group, final boolean recurse, final ThreadGroupPredicate predicate){
    423        Validate.isTrue(group != null, "The group must not be null");
    424        Validate.isTrue(predicate != null, "The predicate must not be null");
    425
    426        int count = group.activeGroupCount();
    427        ThreadGroup[] threadGroups;
    428        do {
    429            threadGroups = new ThreadGroup[count + (count / 2) + 1]; //slightly grow the array size
    430            count = group.enumerate(threadGroups, recurse);
    431            //return value of enumerate() must be strictly less than the array size according to javadoc
    432        } while(count >= threadGroups.length);
    433
    434        final List<ThreadGroup> result = new ArrayList<>(count);
    435        for(int i = 0; i < count; ++i) {
    436            if(predicate.test(threadGroups[i])) {
    437                result.add(threadGroups[i]);
    438            }
    439        }
    440        return Collections.unmodifiableCollection(result);
    441    }
    442}