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.Collection; 021import java.util.Collections; 022import java.util.Objects; 023import java.util.function.Function; 024import java.util.function.Predicate; 025import java.util.stream.Collectors; 026import java.util.stream.Stream; 027 028import org.apache.commons.lang3.time.DurationUtils; 029 030/** 031 * Helpers for {@link Thread} and {@link ThreadGroup}. 032 * 033 * <p> 034 * #ThreadSafe# 035 * </p> 036 * 037 * @see Thread 038 * @see ThreadGroup 039 * @since 3.5 040 */ 041public class ThreadUtils { 042 043 /** 044 * A predicate implementation which always returns true. 045 * 046 * @deprecated Use a {@link Predicate}. 047 */ 048 @Deprecated 049 private static final class AlwaysTruePredicate implements ThreadPredicate, ThreadGroupPredicate { 050 051 private AlwaysTruePredicate() { 052 } 053 054 @Override 055 public boolean test(final Thread thread) { 056 return true; 057 } 058 059 @Override 060 public boolean test(final ThreadGroup threadGroup) { 061 return true; 062 } 063 } 064 065 /** 066 * Used internally, consider private. 067 * <p> 068 * A predicate implementation which matches a thread or thread group name. 069 * </p> 070 * 071 * @deprecated Use a {@link Predicate}. 072 */ 073 @Deprecated 074 public static class NamePredicate implements ThreadPredicate, ThreadGroupPredicate { 075 076 private final String name; 077 078 /** 079 * Constructs an instance. 080 * 081 * @param name thread or thread group name 082 * @throws NullPointerException if the name is {@code null} 083 */ 084 public NamePredicate(final String name) { 085 Objects.requireNonNull(name, "name"); 086 this.name = name; 087 } 088 089 @Override 090 public boolean test(final Thread thread) { 091 return thread != null && thread.getName().equals(name); 092 } 093 094 @Override 095 public boolean test(final ThreadGroup threadGroup) { 096 return threadGroup != null && threadGroup.getName().equals(name); 097 } 098 } 099 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}