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.Collection; 21 import java.util.Collections; 22 import java.util.Objects; 23 import java.util.function.Function; 24 import java.util.function.Predicate; 25 import java.util.stream.Collectors; 26 import java.util.stream.Stream; 27 28 import org.apache.commons.lang3.time.DurationUtils; 29 30 /** 31 * Helpers for {@link Thread} and {@link ThreadGroup}. 32 * 33 * <p> 34 * #ThreadSafe# 35 * </p> 36 * 37 * @see Thread 38 * @see ThreadGroup 39 * @since 3.5 40 */ 41 public class ThreadUtils { 42 43 /** 44 * A predicate implementation which always returns true. 45 * 46 * @deprecated Use a {@link Predicate}. 47 */ 48 @Deprecated 49 private static final class AlwaysTruePredicate implements ThreadPredicate, ThreadGroupPredicate { 50 51 private AlwaysTruePredicate() { 52 } 53 54 @Override 55 public boolean test(final Thread thread) { 56 return true; 57 } 58 59 @Override 60 public boolean test(final ThreadGroup threadGroup) { 61 return true; 62 } 63 } 64 65 /** 66 * Used internally, consider private. 67 * <p> 68 * A predicate implementation which matches a thread or thread group name. 69 * </p> 70 * 71 * @deprecated Use a {@link Predicate}. 72 */ 73 @Deprecated 74 public static class NamePredicate implements ThreadPredicate, ThreadGroupPredicate { 75 76 private final String name; 77 78 /** 79 * Constructs an instance. 80 * 81 * @param name thread or thread group name 82 * @throws NullPointerException if the name is {@code null} 83 */ 84 public NamePredicate(final String name) { 85 Objects.requireNonNull(name, "name"); 86 this.name = name; 87 } 88 89 @Override 90 public boolean test(final Thread thread) { 91 return thread != null && thread.getName().equals(name); 92 } 93 94 @Override 95 public boolean test(final ThreadGroup threadGroup) { 96 return threadGroup != null && threadGroup.getName().equals(name); 97 } 98 } 99 100 /** 101 * A predicate for selecting thread groups. 102 * 103 * @deprecated Use a {@link Predicate}. 104 */ 105 @Deprecated 106 // When breaking BC, replace this with Predicate<ThreadGroup> 107 @FunctionalInterface 108 public interface ThreadGroupPredicate { 109 110 /** 111 * Evaluates this predicate on the given thread group. 112 * @param threadGroup the thread group 113 * @return {@code true} if the threadGroup matches the predicate, otherwise {@code false} 114 */ 115 boolean test(ThreadGroup threadGroup); 116 } 117 118 /** 119 * A predicate implementation which matches a thread id. 120 * 121 * @deprecated Use a {@link Predicate}. 122 */ 123 @Deprecated 124 public static class ThreadIdPredicate implements ThreadPredicate { 125 126 private final long threadId; 127 128 /** 129 * Predicate constructor 130 * 131 * @param threadId the threadId to match 132 * @throws IllegalArgumentException if the threadId is zero or negative 133 */ 134 public ThreadIdPredicate(final long threadId) { 135 if (threadId <= 0) { 136 throw new IllegalArgumentException("The thread id must be greater than zero"); 137 } 138 this.threadId = threadId; 139 } 140 141 @Override 142 public boolean test(final Thread thread) { 143 return thread != null && thread.getId() == threadId; 144 } 145 } 146 147 /** 148 * A predicate for selecting threads. 149 * 150 * @deprecated Use a {@link Predicate}. 151 */ 152 @Deprecated 153 // When breaking BC, replace this with Predicate<Thread> 154 @FunctionalInterface 155 public interface ThreadPredicate { 156 157 /** 158 * Evaluates this predicate on the given thread. 159 * @param thread the thread 160 * @return {@code true} if the thread matches the predicate, otherwise {@code false} 161 */ 162 boolean test(Thread thread); 163 } 164 165 /** 166 * Predicate which always returns true. 167 * 168 * @deprecated Use a {@link Predicate}. 169 */ 170 @Deprecated 171 public static final AlwaysTruePredicate ALWAYS_TRUE_PREDICATE = new AlwaysTruePredicate(); 172 173 private static final Predicate<?> ALWAYS_TRUE = t -> true; 174 175 @SuppressWarnings("unchecked") 176 private static <T> Predicate<T> alwaysTruePredicate() { 177 return (Predicate<T>) ALWAYS_TRUE; 178 } 179 180 /** 181 * Finds the active thread with the specified id. 182 * 183 * @param threadId The thread id 184 * @return The thread with the specified id or {@code null} if no such thread exists 185 * @throws IllegalArgumentException if the specified id is zero or negative 186 * @throws SecurityException 187 * if the current thread cannot access the system thread group 188 * 189 * @throws SecurityException if the current thread cannot modify 190 * thread groups from this thread's thread group up to the system thread group 191 */ 192 public static Thread findThreadById(final long threadId) { 193 if (threadId <= 0) { 194 throw new IllegalArgumentException("The thread id must be greater than zero"); 195 } 196 final Collection<Thread> result = findThreads((Predicate<Thread>) t -> t != null && t.getId() == threadId); 197 return result.isEmpty() ? null : result.iterator().next(); 198 } 199 200 /** 201 * Finds the active thread with the specified id if it belongs to a thread group with the specified group name. 202 * 203 * @param threadId The thread id 204 * @param threadGroupName The thread group name 205 * @return The threads which belongs to a thread group with the specified group name and the thread's id match the specified id. 206 * {@code null} is returned if no such thread exists 207 * @throws NullPointerException if the group name is null 208 * @throws IllegalArgumentException if the specified id is zero or negative 209 * @throws SecurityException 210 * if the current thread cannot access the system thread group 211 * 212 * @throws SecurityException if the current thread cannot modify 213 * thread groups from this thread's thread group up to the system thread group 214 */ 215 public static Thread findThreadById(final long threadId, final String threadGroupName) { 216 Objects.requireNonNull(threadGroupName, "threadGroupName"); 217 final Thread thread = findThreadById(threadId); 218 if (thread != null && thread.getThreadGroup() != null && thread.getThreadGroup().getName().equals(threadGroupName)) { 219 return thread; 220 } 221 return null; 222 } 223 224 /** 225 * Finds the active thread with the specified id if it belongs to the specified thread group. 226 * 227 * @param threadId The thread id 228 * @param threadGroup The thread group 229 * @return The thread which belongs to a specified thread group and the thread's id match the specified id. 230 * {@code null} is returned if no such thread exists 231 * @throws NullPointerException if {@code threadGroup == null} 232 * @throws IllegalArgumentException if the specified id is zero or negative 233 * @throws SecurityException 234 * if the current thread cannot access the system thread group 235 * 236 * @throws SecurityException if the current thread cannot modify 237 * thread groups from this thread's thread group up to the system thread group 238 */ 239 public static Thread findThreadById(final long threadId, final ThreadGroup threadGroup) { 240 Objects.requireNonNull(threadGroup, "threadGroup"); 241 final Thread thread = findThreadById(threadId); 242 if (thread != null && threadGroup.equals(thread.getThreadGroup())) { 243 return thread; 244 } 245 return null; 246 } 247 248 /** 249 * Finds all active thread groups which match the given predicate. 250 * 251 * @param predicate the predicate 252 * @return An unmodifiable {@link Collection} of active thread groups matching the given predicate 253 * @throws NullPointerException if the predicate is null 254 * @throws SecurityException 255 * if the current thread cannot access the system thread group 256 * @throws SecurityException if the current thread cannot modify 257 * thread groups from this thread's thread group up to the system thread group 258 * @since 3.13.0 259 */ 260 public static Collection<ThreadGroup> findThreadGroups(final Predicate<ThreadGroup> predicate) { 261 return findThreadGroups(getSystemThreadGroup(), true, predicate); 262 } 263 264 /** 265 * Finds all active thread groups which match the given predicate and which is a subgroup of the given thread group (or one of its subgroups). 266 * 267 * @param threadGroup the thread group 268 * @param recurse if {@code true} then evaluate the predicate recursively on all thread groups in all subgroups of the given group 269 * @param predicate the predicate 270 * @return An unmodifiable {@link Collection} of active thread groups which match the given predicate and which is a subgroup of the given thread group 271 * @throws NullPointerException if the given group or predicate is null 272 * @throws SecurityException if the current thread cannot modify 273 * thread groups from this thread's thread group up to the system thread group 274 * @since 3.13.0 275 */ 276 public static Collection<ThreadGroup> findThreadGroups(final ThreadGroup threadGroup, final boolean recurse, final Predicate<ThreadGroup> predicate) { 277 Objects.requireNonNull(threadGroup, "threadGroup"); 278 Objects.requireNonNull(predicate, "predicate"); 279 280 int count = threadGroup.activeGroupCount(); 281 ThreadGroup[] threadGroups; 282 do { 283 threadGroups = new ThreadGroup[count + count / 2 + 1]; //slightly grow the array size 284 count = threadGroup.enumerate(threadGroups, recurse); 285 //return value of enumerate() must be strictly less than the array size according to Javadoc 286 } while (count >= threadGroups.length); 287 return Collections.unmodifiableCollection(Stream.of(threadGroups).limit(count).filter(predicate).collect(Collectors.toList())); 288 } 289 290 /** 291 * Finds all active thread groups which match the given predicate and which is a subgroup of the given thread group (or one of its subgroups). 292 * 293 * @param threadGroup the thread group 294 * @param recurse if {@code true} then evaluate the predicate recursively on all thread groups in all subgroups of the given group 295 * @param predicate the predicate 296 * @return An unmodifiable {@link Collection} of active thread groups which match the given predicate and which is a subgroup of the given thread group 297 * @throws NullPointerException if the given group or predicate is null 298 * @throws SecurityException if the current thread cannot modify 299 * thread groups from this thread's thread group up to the system thread group 300 * @deprecated Use {@link #findThreadGroups(ThreadGroup, boolean, Predicate)}. 301 */ 302 @Deprecated 303 public static Collection<ThreadGroup> findThreadGroups(final ThreadGroup threadGroup, final boolean recurse, final ThreadGroupPredicate predicate) { 304 return findThreadGroups(threadGroup, recurse, (Predicate<ThreadGroup>) predicate::test); 305 } 306 307 /** 308 * Finds all active thread groups which match the given predicate. 309 * 310 * @param predicate the predicate 311 * @return An unmodifiable {@link Collection} of active thread groups matching the given predicate 312 * @throws NullPointerException if the predicate is null 313 * @throws SecurityException 314 * if the current thread cannot access the system thread group 315 * @throws SecurityException if the current thread cannot modify 316 * thread groups from this thread's thread group up to the system thread group 317 * @deprecated Use {@link #findThreadGroups(Predicate)}. 318 */ 319 @Deprecated 320 public static Collection<ThreadGroup> findThreadGroups(final ThreadGroupPredicate predicate) { 321 return findThreadGroups(getSystemThreadGroup(), true, predicate); 322 } 323 324 /** 325 * Finds active thread groups with the specified group name. 326 * 327 * @param threadGroupName The thread group name 328 * @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. 329 * @throws NullPointerException if group 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<ThreadGroup> findThreadGroupsByName(final String threadGroupName) { 337 return findThreadGroups(predicateThreadGroup(threadGroupName)); 338 } 339 340 /** 341 * Finds all active threads which match the given predicate. 342 * 343 * @param predicate the predicate 344 * @return An unmodifiable {@link Collection} of active threads matching the given predicate 345 * 346 * @throws NullPointerException if the predicate is null 347 * @throws SecurityException 348 * if the current thread cannot access the system thread group 349 * @throws SecurityException if the current thread cannot modify 350 * thread groups from this thread's thread group up to the system thread group 351 * @since 3.13.0 352 */ 353 public static Collection<Thread> findThreads(final Predicate<Thread> predicate) { 354 return findThreads(getSystemThreadGroup(), true, predicate); 355 } 356 357 /** 358 * Finds all active threads which match the given predicate and which belongs to the given thread group (or one of its subgroups). 359 * 360 * @param threadGroup the thread group 361 * @param recurse if {@code true} then evaluate the predicate recursively on all threads in all subgroups of the given group 362 * @param predicate the predicate 363 * @return An unmodifiable {@link Collection} of active threads which match the given predicate and which belongs to the given thread group 364 * @throws NullPointerException if the given group or predicate is null 365 * @throws SecurityException if the current thread cannot modify 366 * thread groups from this thread's thread group up to the system thread group 367 * @since 3.13.0 368 */ 369 public static Collection<Thread> findThreads(final ThreadGroup threadGroup, final boolean recurse, final Predicate<Thread> predicate) { 370 Objects.requireNonNull(threadGroup, "The group must not be null"); 371 Objects.requireNonNull(predicate, "The predicate must not be null"); 372 int count = threadGroup.activeCount(); 373 Thread[] threads; 374 do { 375 threads = new Thread[count + count / 2 + 1]; //slightly grow the array size 376 count = threadGroup.enumerate(threads, recurse); 377 //return value of enumerate() must be strictly less than the array size according to javadoc 378 } while (count >= threads.length); 379 return Collections.unmodifiableCollection(Stream.of(threads).limit(count).filter(predicate).collect(Collectors.toList())); 380 } 381 382 /** 383 * Finds all active threads which match the given predicate and which belongs to the given thread group (or one of its subgroups). 384 * 385 * @param threadGroup the thread group 386 * @param recurse if {@code true} then evaluate the predicate recursively on all threads in all subgroups of the given group 387 * @param predicate the predicate 388 * @return An unmodifiable {@link Collection} of active threads which match the given predicate and which belongs to the given thread group 389 * @throws NullPointerException if the given group or predicate is null 390 * @throws SecurityException if the current thread cannot modify 391 * thread groups from this thread's thread group up to the system thread group 392 * @deprecated Use {@link #findThreads(ThreadGroup, boolean, Predicate)}. 393 */ 394 @Deprecated 395 public static Collection<Thread> findThreads(final ThreadGroup threadGroup, final boolean recurse, final ThreadPredicate predicate) { 396 return findThreads(threadGroup, recurse, (Predicate<Thread>) predicate::test); 397 } 398 399 /** 400 * Finds all active threads which match the given predicate. 401 * 402 * @param predicate the predicate 403 * @return An unmodifiable {@link Collection} of active threads matching the given predicate 404 * 405 * @throws NullPointerException if the predicate is null 406 * @throws SecurityException 407 * if the current thread cannot access the system thread group 408 * @throws SecurityException if the current thread cannot modify 409 * thread groups from this thread's thread group up to the system thread group 410 * @deprecated Use {@link #findThreads(Predicate)}. 411 */ 412 @Deprecated 413 public static Collection<Thread> findThreads(final ThreadPredicate predicate) { 414 return findThreads(getSystemThreadGroup(), true, predicate); 415 } 416 417 /** 418 * Finds active threads with the specified name. 419 * 420 * @param threadName The thread name 421 * @return The threads with the specified name or an empty collection if no such thread exists. The collection returned is always unmodifiable. 422 * @throws NullPointerException if the specified name is null 423 * @throws SecurityException 424 * if the current thread cannot access the system thread group 425 * 426 * @throws SecurityException if the current thread cannot modify 427 * thread groups from this thread's thread group up to the system thread group 428 */ 429 public static Collection<Thread> findThreadsByName(final String threadName) { 430 return findThreads(predicateThread(threadName)); 431 } 432 433 /** 434 * Finds active threads with the specified name if they belong to a thread group with the specified group name. 435 * 436 * @param threadName The thread name 437 * @param threadGroupName The thread group name 438 * @return The threads which belongs to a thread group with the specified group name and the thread's name match the specified name, 439 * An empty collection is returned if no such thread exists. The collection returned is always unmodifiable. 440 * @throws NullPointerException if the specified thread name or group name is null 441 * @throws SecurityException 442 * if the current thread cannot access the system thread group 443 * 444 * @throws SecurityException if the current thread cannot modify 445 * thread groups from this thread's thread group up to the system thread group 446 */ 447 public static Collection<Thread> findThreadsByName(final String threadName, final String threadGroupName) { 448 Objects.requireNonNull(threadName, "threadName"); 449 Objects.requireNonNull(threadGroupName, "threadGroupName"); 450 return Collections.unmodifiableCollection(findThreadGroups(predicateThreadGroup(threadGroupName)).stream() 451 .flatMap(group -> findThreads(group, false, predicateThread(threadName)).stream()).collect(Collectors.toList())); 452 } 453 454 /** 455 * Finds active threads with the specified name if they belong to a specified thread group. 456 * 457 * @param threadName The thread name 458 * @param threadGroup The thread group 459 * @return The threads which belongs to a thread group and the thread's name match the specified name, 460 * An empty collection is returned if no such thread exists. The collection returned is always unmodifiable. 461 * @throws NullPointerException if the specified thread name or group is null 462 * @throws SecurityException 463 * if the current thread cannot access the system thread group 464 * 465 * @throws SecurityException if the current thread cannot modify 466 * thread groups from this thread's thread group up to the system thread group 467 */ 468 public static Collection<Thread> findThreadsByName(final String threadName, final ThreadGroup threadGroup) { 469 return findThreads(threadGroup, false, predicateThread(threadName)); 470 } 471 472 /** 473 * Gets all active thread groups excluding the system thread group (A thread group is active if it has been not destroyed). 474 * 475 * @return all thread groups excluding the system thread group. The collection returned is always unmodifiable. 476 * @throws SecurityException 477 * if the current thread cannot access the system thread group 478 * 479 * @throws SecurityException if the current thread cannot modify 480 * thread groups from this thread's thread group up to the system thread group 481 */ 482 public static Collection<ThreadGroup> getAllThreadGroups() { 483 return findThreadGroups(alwaysTruePredicate()); 484 } 485 486 /** 487 * Gets all active threads (A thread is active if it has been started and has not yet died). 488 * 489 * @return all active threads. The collection returned is always unmodifiable. 490 * @throws SecurityException 491 * if the current thread cannot access the system thread group 492 * 493 * @throws SecurityException if the current thread cannot modify 494 * thread groups from this thread's thread group up to the system thread group 495 */ 496 public static Collection<Thread> getAllThreads() { 497 return findThreads(alwaysTruePredicate()); 498 } 499 500 /** 501 * Gets the system thread group (sometimes also referred as "root thread group"). 502 * <p> 503 * This method returns null if this thread has died (been stopped). 504 * </p> 505 * 506 * @return the system thread group 507 * @throws SecurityException if the current thread cannot modify thread groups from this thread's thread group up to the 508 * system thread group 509 */ 510 public static ThreadGroup getSystemThreadGroup() { 511 ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); 512 while (threadGroup != null && threadGroup.getParent() != null) { 513 threadGroup = threadGroup.getParent(); 514 } 515 return threadGroup; 516 } 517 518 /** 519 * Waits for the given thread to die for the given duration. Implemented using {@link Thread#join(long, int)}. 520 * 521 * @param thread The thread to join. 522 * @param duration How long to wait. 523 * @throws InterruptedException if any thread has interrupted the current thread. 524 * @see Thread#join(long, int) 525 * @since 3.12.0 526 */ 527 public static void join(final Thread thread, final Duration duration) throws InterruptedException { 528 DurationUtils.accept(thread::join, duration); 529 } 530 531 private static <T> Predicate<T> namePredicate(final String name, final Function<T, String> nameGetter) { 532 return (Predicate<T>) t -> t != null && Objects.equals(nameGetter.apply(t), Objects.requireNonNull(name)); 533 } 534 535 private static Predicate<Thread> predicateThread(final String threadName) { 536 return namePredicate(threadName, Thread::getName); 537 } 538 539 private static Predicate<ThreadGroup> predicateThreadGroup(final String threadGroupName) { 540 return namePredicate(threadGroupName, ThreadGroup::getName); 541 } 542 543 /** 544 * Sleeps the current thread for the given duration. Implemented using {@link Thread#sleep(long, int)}. 545 * 546 * @param duration How long to sleep. 547 * @throws InterruptedException if any thread has interrupted the current thread. 548 * @see Thread#sleep(long, int) 549 * @since 3.12.0 550 */ 551 public static void sleep(final Duration duration) throws InterruptedException { 552 DurationUtils.accept(Thread::sleep, duration); 553 } 554 555 /** 556 * Sleeps for the given duration while ignoring {@link InterruptedException}. 557 * <p> 558 * The sleep duration may be shorter than duration if we catch a {@link InterruptedException}. 559 * </p> 560 * 561 * @param duration the length of time to sleep. 562 * @since 3.13.0 563 */ 564 public static void sleepQuietly(final Duration duration) { 565 try { 566 sleep(duration); 567 } catch (final InterruptedException ignore) { 568 // Ignore & be quiet. 569 } 570 } 571 572 /** 573 * ThreadUtils instances should NOT be constructed in standard programming. Instead, the class should be used as {@code ThreadUtils.getAllThreads()} 574 * <p> 575 * This constructor is public to permit tools that require a JavaBean instance to operate. 576 * </p> 577 * 578 * @deprecated TODO Make private in 4.0. 579 */ 580 @Deprecated 581 public ThreadUtils() { 582 // empty 583 } 584 }