    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     * Finds 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.notNull(threadGroup, "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     * Finds 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.notNull(threadGroupName, "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     * Finds 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     * Finds 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.notNull(threadName, "The thread name must not be null");
    118        Validate.notNull(threadGroupName, "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     * Finds 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     * Gets 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     * Gets 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     * Gets 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     * Finds 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     * Finds 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    // When breaking BC, replace this with Predicate<Thread>
    243    @FunctionalInterface
    244    public interface ThreadPredicate {
    245
    246        /**
    247         * Evaluates this predicate on the given thread.
    248         * @param thread the thread
    249         * @return {@code true} if the thread matches the predicate, otherwise {@code false}
    250         */
    251        boolean test(Thread thread);
    252    }
    253
    254    /**
    255     * A predicate for selecting threadgroups.
    256     */
    257    // When breaking BC, replace this with Predicate<ThreadGroup>
    258    @FunctionalInterface
    259    public interface ThreadGroupPredicate {
    260
    261        /**
    262         * Evaluates this predicate on the given threadgroup.
    263         * @param threadGroup the threadgroup
    264         * @return {@code true} if the threadGroup matches the predicate, otherwise {@code false}
    265         */
    266        boolean test(ThreadGroup threadGroup);
    267    }
    268
    269    /**
    270     * Predicate which always returns true.
    271     */
    272    public static final AlwaysTruePredicate ALWAYS_TRUE_PREDICATE = new AlwaysTruePredicate();
    273
    274    /**
    275     * A predicate implementation which always returns true.
    276     */
    277    private static final class AlwaysTruePredicate implements ThreadPredicate, ThreadGroupPredicate {
    278
    279        private AlwaysTruePredicate() {
    280        }
    281
    282        @Override
    283        public boolean test(final ThreadGroup threadGroup) {
    284            return true;
    285        }
    286
    287        @Override
    288        public boolean test(final Thread thread) {
    289            return true;
    290        }
    291    }
    292
    293    /**
    294     * A predicate implementation which matches a thread or threadgroup name.
    295     */
    296    public static class NamePredicate implements ThreadPredicate, ThreadGroupPredicate {
    297
    298        private final String name;
    299
    300        /**
    301         * Predicate constructor
    302         *
    303         * @param name thread or threadgroup name
    304         * @throws IllegalArgumentException if the name is {@code null}
    305         */
    306        public NamePredicate(final String name) {
    307            super();
    308            Validate.notNull(name, "The name must not be null");
    309            this.name = name;
    310        }
    311
    312        @Override
    313        public boolean test(final ThreadGroup threadGroup) {
    314            return threadGroup != null && threadGroup.getName().equals(name);
    315        }
    316
    317        @Override
    318        public boolean test(final Thread thread) {
    319            return thread != null && thread.getName().equals(name);
    320        }
    321    }
    322
    323    /**
    324     * A predicate implementation which matches a thread id.
    325     */
    326    public static class ThreadIdPredicate implements ThreadPredicate {
    327
    328        private final long threadId;
    329
    330        /**
    331         * Predicate constructor
    332         *
    333         * @param threadId the threadId to match
    334         * @throws IllegalArgumentException if the threadId is zero or negative
    335         */
    336        public ThreadIdPredicate(final long threadId) {
    337            super();
    338            if (threadId <= 0) {
    339                throw new IllegalArgumentException("The thread id must be greater than zero");
    340            }
    341            this.threadId = threadId;
    342        }
    343
    344        @Override
    345        public boolean test(final Thread thread) {
    346            return thread != null && thread.getId() == threadId;
    347        }
    348    }
    349
    350    /**
    351     * Select all active threads which match the given predicate.
    352     *
    353     * @param predicate the predicate
    354     * @return An unmodifiable {@code Collection} of active threads matching the given predicate
    355     *
    356     * @throws IllegalArgumentException if the predicate is null
    357     * @throws  SecurityException
    358     *          if the current thread cannot access the system thread group
    359     * @throws  SecurityException  if the current thread cannot modify
    360     *          thread groups from this thread's thread group up to the system thread group
    361     */
    362    public static Collection<Thread> findThreads(final ThreadPredicate predicate) {
    363        return findThreads(getSystemThreadGroup(), true, predicate);
    364    }
    365
    366    /**
    367     * Select all active threadgroups which match the given predicate.
    368     *
    369     * @param predicate the predicate
    370     * @return An unmodifiable {@code Collection} of active threadgroups matching the given predicate
    371     * @throws IllegalArgumentException if the predicate is null
    372     * @throws  SecurityException
    373     *          if the current thread cannot access the system thread group
    374     * @throws  SecurityException  if the current thread cannot modify
    375     *          thread groups from this thread's thread group up to the system thread group
    376     */
    377    public static Collection<ThreadGroup> findThreadGroups(final ThreadGroupPredicate predicate) {
    378        return findThreadGroups(getSystemThreadGroup(), true, predicate);
    379    }
    380
    381    /**
    382     * Select all active threads which match the given predicate and which belongs to the given thread group (or one of its subgroups).
    383     *
    384     * @param group the thread group
    385     * @param recurse if {@code true} then evaluate the predicate recursively on all threads in all subgroups of the given group
    386     * @param predicate the predicate
    387     * @return An unmodifiable {@code Collection} of active threads which match the given predicate and which belongs to the given thread group
    388     * @throws IllegalArgumentException if the given group or predicate is null
    389     * @throws  SecurityException  if the current thread cannot modify
    390     *          thread groups from this thread's thread group up to the system thread group
    391     */
    392    public static Collection<Thread> findThreads(final ThreadGroup group, final boolean recurse, final ThreadPredicate predicate) {
    393        Validate.notNull(group, "The group must not be null");
    394        Validate.notNull(predicate, "The predicate must not be null");
    395
    396        int count = group.activeCount();
    397        Thread[] threads;
    398        do {
    399            threads = new Thread[count + (count / 2) + 1]; //slightly grow the array size
    400            count = group.enumerate(threads, recurse);
    401            //return value of enumerate() must be strictly less than the array size according to javadoc
    402        } while (count >= threads.length);
    403
    404        final List<Thread> result = new ArrayList<>(count);
    405        for (int i = 0; i < count; ++i) {
    406            if (predicate.test(threads[i])) {
    407                result.add(threads[i]);
    408            }
    409        }
    410        return Collections.unmodifiableCollection(result);
    411    }
    412
    413    /**
    414     * Select all active threadgroups which match the given predicate and which is a subgroup of the given thread group (or one of its subgroups).
    415     *
    416     * @param group the thread group
    417     * @param recurse if {@code true} then evaluate the predicate recursively on all threadgroups in all subgroups of the given group
    418     * @param predicate the predicate
    419     * @return An unmodifiable {@code Collection} of active threadgroups which match the given predicate and which is a subgroup of the given thread group
    420     * @throws IllegalArgumentException if the given group or predicate is null
    421     * @throws  SecurityException  if the current thread cannot modify
    422     *          thread groups from this thread's thread group up to the system thread group
    423     */
    424    public static Collection<ThreadGroup> findThreadGroups(final ThreadGroup group, final boolean recurse, final ThreadGroupPredicate predicate) {
    425        Validate.notNull(group, "The group must not be null");
    426        Validate.notNull(predicate, "The predicate must not be null");
    427
    428        int count = group.activeGroupCount();
    429        ThreadGroup[] threadGroups;
    430        do {
    431            threadGroups = new ThreadGroup[count + (count / 2) + 1]; //slightly grow the array size
    432            count = group.enumerate(threadGroups, recurse);
    433            //return value of enumerate() must be strictly less than the array size according to javadoc
    434        } while (count >= threadGroups.length);
    435
    436        final List<ThreadGroup> result = new ArrayList<>(count);
    437        for (int i = 0; i < count; ++i) {
    438            if (predicate.test(threadGroups[i])) {
    439                result.add(threadGroups[i]);
    440            }
    441        }
    442        return Collections.unmodifiableCollection(result);
    443    }
    444}