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}