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.time.Duration; 020import java.util.ArrayList; 021import java.util.Collection; 022import java.util.Collections; 023import java.util.List; 024 025import org.apache.commons.lang3.time.DurationUtils; 026 027/** 028 * <p> 029 * Helpers for {@code java.lang.Thread} and {@code java.lang.ThreadGroup}. 030 * </p> 031 * <p> 032 * #ThreadSafe# 033 * </p> 034 * 035 * @see java.lang.Thread 036 * @see java.lang.ThreadGroup 037 * @since 3.5 038 */ 039public class ThreadUtils { 040 041 /** 042 * A predicate implementation which always returns true. 043 */ 044 private static final class AlwaysTruePredicate implements ThreadPredicate, ThreadGroupPredicate { 045 046 private AlwaysTruePredicate() { 047 } 048 049 @Override 050 public boolean test(final Thread thread) { 051 return true; 052 } 053 054 @Override 055 public boolean test(final ThreadGroup threadGroup) { 056 return true; 057 } 058 } 059 060 /** 061 * A predicate implementation which matches a thread or threadgroup name. 062 */ 063 public static class NamePredicate implements ThreadPredicate, ThreadGroupPredicate { 064 065 private final String name; 066 067 /** 068 * Predicate constructor 069 * 070 * @param name thread or threadgroup name 071 * @throws IllegalArgumentException if the name is {@code null} 072 */ 073 public NamePredicate(final String name) { 074 Validate.notNull(name, "name"); 075 this.name = name; 076 } 077 078 @Override 079 public boolean test(final Thread thread) { 080 return thread != null && thread.getName().equals(name); 081 } 082 083 @Override 084 public boolean test(final ThreadGroup threadGroup) { 085 return threadGroup != null && threadGroup.getName().equals(name); 086 } 087 } 088 089 /** 090 * A predicate for selecting threadgroups. 091 */ 092 // When breaking BC, replace this with Predicate<ThreadGroup> 093 @FunctionalInterface 094 public interface ThreadGroupPredicate { 095 096 /** 097 * Evaluates this predicate on the given threadgroup. 098 * @param threadGroup the threadgroup 099 * @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}