View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang3;
18  
19  import java.time.Duration;
20  import java.util.ArrayList;
21  import java.util.Collection;
22  import java.util.Collections;
23  import java.util.List;
24  
25  import org.apache.commons.lang3.time.DurationUtils;
26  
27  /**
28   * <p>
29   * Helpers for {@code java.lang.Thread} and {@code java.lang.ThreadGroup}.
30   * </p>
31   * <p>
32   * #ThreadSafe#
33   * </p>
34   *
35   * @see java.lang.Thread
36   * @see java.lang.ThreadGroup
37   * @since 3.5
38   */
39  public class ThreadUtils {
40  
41      /**
42       * A predicate implementation which always returns true.
43       */
44      private static final class AlwaysTruePredicate implements ThreadPredicate, ThreadGroupPredicate {
45  
46          private AlwaysTruePredicate() {
47          }
48  
49          @Override
50          public boolean test(final Thread thread) {
51              return true;
52          }
53  
54          @Override
55          public boolean test(final ThreadGroup threadGroup) {
56              return true;
57          }
58      }
59  
60      /**
61       * A predicate implementation which matches a thread or threadgroup name.
62       */
63      public static class NamePredicate implements ThreadPredicate, ThreadGroupPredicate {
64  
65          private final String name;
66  
67          /**
68           * Predicate constructor
69           *
70           * @param name thread or threadgroup name
71           * @throws IllegalArgumentException if the name is {@code null}
72           */
73          public NamePredicate(final String name) {
74              Validate.notNull(name, "name");
75              this.name = name;
76          }
77  
78          @Override
79          public boolean test(final Thread thread) {
80              return thread != null && thread.getName().equals(name);
81          }
82  
83          @Override
84          public boolean test(final ThreadGroup threadGroup) {
85              return threadGroup != null && threadGroup.getName().equals(name);
86          }
87      }
88  
89      /**
90       * A predicate for selecting threadgroups.
91       */
92      // When breaking BC, replace this with Predicate<ThreadGroup>
93      @FunctionalInterface
94      public interface ThreadGroupPredicate {
95  
96          /**
97           * Evaluates this predicate on the given threadgroup.
98           * @param threadGroup the threadgroup
99           * @return {@code true} if the threadGroup matches the predicate, otherwise {@code false}
100          */
101         boolean test(ThreadGroup threadGroup);
102     }
103 
104     /**
105      * A predicate implementation which matches a thread id.
106      */
107     public static class ThreadIdPredicate implements ThreadPredicate {
108 
109         private final long threadId;
110 
111         /**
112          * Predicate constructor
113          *
114          * @param threadId the threadId to match
115          * @throws IllegalArgumentException if the threadId is zero or negative
116          */
117         public ThreadIdPredicate(final long threadId) {
118             if (threadId <= 0) {
119                 throw new IllegalArgumentException("The thread id must be greater than zero");
120             }
121             this.threadId = threadId;
122         }
123 
124         @Override
125         public boolean test(final Thread thread) {
126             return thread != null && thread.getId() == threadId;
127         }
128     }
129 
130     /**
131      * A predicate for selecting threads.
132      */
133     // When breaking BC, replace this with Predicate<Thread>
134     @FunctionalInterface
135     public interface ThreadPredicate {
136 
137         /**
138          * Evaluates this predicate on the given thread.
139          * @param thread the thread
140          * @return {@code true} if the thread matches the predicate, otherwise {@code false}
141          */
142         boolean test(Thread thread);
143     }
144 
145     /**
146      * Predicate which always returns true.
147      */
148     public static final AlwaysTruePredicate ALWAYS_TRUE_PREDICATE = new AlwaysTruePredicate();
149 
150     /**
151      * Finds the active thread with the specified id.
152      *
153      * @param threadId The thread id
154      * @return The thread with the specified id or {@code null} if no such thread exists
155      * @throws IllegalArgumentException if the specified id is zero or negative
156      * @throws  SecurityException
157      *          if the current thread cannot access the system thread group
158      *
159      * @throws  SecurityException  if the current thread cannot modify
160      *          thread groups from this thread's thread group up to the system thread group
161      */
162     public static Thread findThreadById(final long threadId) {
163         final Collection<Thread> result = findThreads(new ThreadIdPredicate(threadId));
164         return result.isEmpty() ? null : result.iterator().next();
165     }
166 
167     /**
168      * Finds the active thread with the specified id if it belongs to a thread group with the specified group name.
169      *
170      * @param threadId The thread id
171      * @param threadGroupName The thread group name
172      * @return The threads which belongs to a thread group with the specified group name and the thread's id match the specified id.
173      * {@code null} is returned if no such thread exists
174      * @throws IllegalArgumentException if the specified id is zero or negative or the group name is null
175      * @throws  SecurityException
176      *          if the current thread cannot access the system thread group
177      *
178      * @throws  SecurityException  if the current thread cannot modify
179      *          thread groups from this thread's thread group up to the system thread group
180      */
181     public static Thread findThreadById(final long threadId, final String threadGroupName) {
182         Validate.notNull(threadGroupName, "threadGroupName");
183         final Thread thread = findThreadById(threadId);
184         if (thread != null && thread.getThreadGroup() != null && thread.getThreadGroup().getName().equals(threadGroupName)) {
185             return thread;
186         }
187         return null;
188     }
189 
190     /**
191      * Finds the active thread with the specified id if it belongs to the specified thread group.
192      *
193      * @param threadId The thread id
194      * @param threadGroup The thread group
195      * @return The thread which belongs to a specified thread group and the thread's id match the specified id.
196      * {@code null} is returned if no such thread exists
197      * @throws IllegalArgumentException if the specified id is zero or negative or the group is null
198      * @throws  SecurityException
199      *          if the current thread cannot access the system thread group
200      *
201      * @throws  SecurityException  if the current thread cannot modify
202      *          thread groups from this thread's thread group up to the system thread group
203      */
204     public static Thread findThreadById(final long threadId, final ThreadGroup threadGroup) {
205         Validate.notNull(threadGroup, "threadGroup");
206         final Thread thread = findThreadById(threadId);
207         if (thread != null && threadGroup.equals(thread.getThreadGroup())) {
208             return thread;
209         }
210         return null;
211     }
212 
213     /**
214      * Select all active threadgroups which match the given predicate and which is a subgroup of the given thread group (or one of its subgroups).
215      *
216      * @param group the thread group
217      * @param recurse if {@code true} then evaluate the predicate recursively on all threadgroups in all subgroups of the given group
218      * @param predicate the predicate
219      * @return An unmodifiable {@code Collection} of active threadgroups which match the given predicate and which is a subgroup of the given thread group
220      * @throws IllegalArgumentException if the given group or predicate is null
221      * @throws  SecurityException  if the current thread cannot modify
222      *          thread groups from this thread's thread group up to the system thread group
223      */
224     public static Collection<ThreadGroup> findThreadGroups(final ThreadGroup group, final boolean recurse, final ThreadGroupPredicate predicate) {
225         Validate.notNull(group, "group");
226         Validate.notNull(predicate, "predicate");
227 
228         int count = group.activeGroupCount();
229         ThreadGroup[] threadGroups;
230         do {
231             threadGroups = new ThreadGroup[count + (count / 2) + 1]; //slightly grow the array size
232             count = group.enumerate(threadGroups, recurse);
233             //return value of enumerate() must be strictly less than the array size according to javadoc
234         } while (count >= threadGroups.length);
235 
236         final List<ThreadGroup> result = new ArrayList<>(count);
237         for (int i = 0; i < count; ++i) {
238             if (predicate.test(threadGroups[i])) {
239                 result.add(threadGroups[i]);
240             }
241         }
242         return Collections.unmodifiableCollection(result);
243     }
244 
245     /**
246      * Select all active threadgroups which match the given predicate.
247      *
248      * @param predicate the predicate
249      * @return An unmodifiable {@code Collection} of active threadgroups matching the given predicate
250      * @throws IllegalArgumentException if the predicate is null
251      * @throws  SecurityException
252      *          if the current thread cannot access the system thread group
253      * @throws  SecurityException  if the current thread cannot modify
254      *          thread groups from this thread's thread group up to the system thread group
255      */
256     public static Collection<ThreadGroup> findThreadGroups(final ThreadGroupPredicate predicate) {
257         return findThreadGroups(getSystemThreadGroup(), true, predicate);
258     }
259 
260     /**
261      * Finds active thread groups with the specified group name.
262      *
263      * @param threadGroupName The thread group name
264      * @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.
265      * @throws IllegalArgumentException if group name is null
266      * @throws  SecurityException
267      *          if the current thread cannot access the system thread group
268      *
269      * @throws  SecurityException  if the current thread cannot modify
270      *          thread groups from this thread's thread group up to the system thread group
271      */
272     public static Collection<ThreadGroup> findThreadGroupsByName(final String threadGroupName) {
273         return findThreadGroups(new NamePredicate(threadGroupName));
274     }
275 
276     /**
277      * Select all active threads which match the given predicate and which belongs to the given thread group (or one of its subgroups).
278      *
279      * @param group the thread group
280      * @param recurse if {@code true} then evaluate the predicate recursively on all threads in all subgroups of the given group
281      * @param predicate the predicate
282      * @return An unmodifiable {@code Collection} of active threads which match the given predicate and which belongs to the given thread group
283      * @throws IllegalArgumentException if the given group or predicate is null
284      * @throws  SecurityException  if the current thread cannot modify
285      *          thread groups from this thread's thread group up to the system thread group
286      */
287     public static Collection<Thread> findThreads(final ThreadGroup group, final boolean recurse, final ThreadPredicate predicate) {
288         Validate.notNull(group, "The group must not be null");
289         Validate.notNull(predicate, "The predicate must not be null");
290 
291         int count = group.activeCount();
292         Thread[] threads;
293         do {
294             threads = new Thread[count + (count / 2) + 1]; //slightly grow the array size
295             count = group.enumerate(threads, recurse);
296             //return value of enumerate() must be strictly less than the array size according to javadoc
297         } while (count >= threads.length);
298 
299         final List<Thread> result = new ArrayList<>(count);
300         for (int i = 0; i < count; ++i) {
301             if (predicate.test(threads[i])) {
302                 result.add(threads[i]);
303             }
304         }
305         return Collections.unmodifiableCollection(result);
306     }
307 
308     /**
309      * Select all active threads which match the given predicate.
310      *
311      * @param predicate the predicate
312      * @return An unmodifiable {@code Collection} of active threads matching the given predicate
313      *
314      * @throws IllegalArgumentException if the predicate is null
315      * @throws  SecurityException
316      *          if the current thread cannot access the system thread group
317      * @throws  SecurityException  if the current thread cannot modify
318      *          thread groups from this thread's thread group up to the system thread group
319      */
320     public static Collection<Thread> findThreads(final ThreadPredicate predicate) {
321         return findThreads(getSystemThreadGroup(), true, predicate);
322     }
323 
324     /**
325      * Finds active threads with the specified name.
326      *
327      * @param threadName The thread name
328      * @return The threads with the specified name or an empty collection if no such thread exists. The collection returned is always unmodifiable.
329      * @throws IllegalArgumentException if the specified name is null
330      * @throws  SecurityException
331      *          if the current thread cannot access the system thread group
332      *
333      * @throws  SecurityException  if the current thread cannot modify
334      *          thread groups from this thread's thread group up to the system thread group
335      */
336     public static Collection<Thread> findThreadsByName(final String threadName) {
337         return findThreads(new NamePredicate(threadName));
338     }
339 
340     /**
341      * Finds active threads with the specified name if they belong to a thread group with the specified group name.
342      *
343      * @param threadName The thread name
344      * @param threadGroupName The thread group name
345      * @return The threads which belongs to a thread group with the specified group name and the thread's name match the specified name,
346      * An empty collection is returned if no such thread exists. The collection returned is always unmodifiable.
347      * @throws IllegalArgumentException if the specified thread name or group name is null
348      * @throws  SecurityException
349      *          if the current thread cannot access the system thread group
350      *
351      * @throws  SecurityException  if the current thread cannot modify
352      *          thread groups from this thread's thread group up to the system thread group
353      */
354     public static Collection<Thread> findThreadsByName(final String threadName, final String threadGroupName) {
355         Validate.notNull(threadName, "threadName");
356         Validate.notNull(threadGroupName, "threadGroupName");
357 
358         final Collection<ThreadGroup> threadGroups = findThreadGroups(new NamePredicate(threadGroupName));
359 
360         if (threadGroups.isEmpty()) {
361             return Collections.emptyList();
362         }
363 
364         final Collection<Thread> result = new ArrayList<>();
365         final NamePredicate threadNamePredicate = new NamePredicate(threadName);
366         for (final ThreadGroup group : threadGroups) {
367             result.addAll(findThreads(group, false, threadNamePredicate));
368         }
369         return Collections.unmodifiableCollection(result);
370     }
371 
372     /**
373      * Finds active threads with the specified name if they belong to a specified thread group.
374      *
375      * @param threadName The thread name
376      * @param threadGroup The thread group
377      * @return The threads which belongs to a thread group and the thread's name match the specified name,
378      * An empty collection is returned if no such thread exists. The collection returned is always unmodifiable.
379      * @throws IllegalArgumentException if the specified thread name or group is null
380      * @throws  SecurityException
381      *          if the current thread cannot access the system thread group
382      *
383      * @throws  SecurityException  if the current thread cannot modify
384      *          thread groups from this thread's thread group up to the system thread group
385      */
386     public static Collection<Thread> findThreadsByName(final String threadName, final ThreadGroup threadGroup) {
387         return findThreads(threadGroup, false, new NamePredicate(threadName));
388     }
389 
390     /**
391      * Gets all active thread groups excluding the system thread group (A thread group is active if it has been not destroyed).
392      *
393      * @return all thread groups excluding the system thread group. The collection returned is always unmodifiable.
394      * @throws  SecurityException
395      *          if the current thread cannot access the system thread group
396      *
397      * @throws  SecurityException  if the current thread cannot modify
398      *          thread groups from this thread's thread group up to the system thread group
399      */
400     public static Collection<ThreadGroup> getAllThreadGroups() {
401         return findThreadGroups(ALWAYS_TRUE_PREDICATE);
402     }
403 
404     /**
405      * Gets all active threads (A thread is active if it has been started and has not yet died).
406      *
407      * @return all active threads. The collection returned is always unmodifiable.
408      * @throws  SecurityException
409      *          if the current thread cannot access the system thread group
410      *
411      * @throws  SecurityException  if the current thread cannot modify
412      *          thread groups from this thread's thread group up to the system thread group
413      */
414     public static Collection<Thread> getAllThreads() {
415         return findThreads(ALWAYS_TRUE_PREDICATE);
416     }
417 
418     /**
419      * Gets the system thread group (sometimes also referred as "root thread group").
420      *
421      * @return the system thread group
422      * @throws  SecurityException  if the current thread cannot modify
423      *          thread groups from this thread's thread group up to the system thread group
424      */
425     public static ThreadGroup getSystemThreadGroup() {
426         ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
427         while (threadGroup.getParent() != null) {
428             threadGroup = threadGroup.getParent();
429         }
430         return threadGroup;
431     }
432 
433     /**
434      * Waits for the given thread to die for the given duration. Implemented using {@link Thread#join(long, int)}.
435      *
436      * @param thread The thread to join.
437      * @param duration How long to wait.
438      * @throws InterruptedException if any thread has interrupted the current thread.
439      * @see Thread#join(long, int)
440      * @since 3.12.0
441      */
442     public static void join(final Thread thread, final Duration duration) throws InterruptedException {
443         DurationUtils.accept(thread::join, duration);
444     }
445 
446     /**
447      * Sleeps the current thread for the given duration. Implemented using {@link Thread#sleep(long, int)}.
448      *
449      * @param duration How long to sleep.
450      * @throws InterruptedException if any thread has interrupted the current thread.
451      * @see Thread#sleep(long, int)
452      * @since 3.12.0
453      */
454     public static void sleep(final Duration duration) throws InterruptedException {
455         DurationUtils.accept(Thread::sleep, duration);
456     }
457 
458     /**
459      * <p>
460      * ThreadUtils instances should NOT be constructed in standard programming. Instead, the class should be used as
461      * {@code ThreadUtils.getAllThreads()}
462      * </p>
463      * <p>
464      * This constructor is public to permit tools that require a JavaBean instance to operate.
465      * </p>
466      */
467     public ThreadUtils() {
468     }
469 }