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 * Return 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.isTrue(threadGroup != null, "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 * Return 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.isTrue(threadGroupName != null, "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 * Return 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 * Return 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.isTrue(threadName != null, "The thread name must not be null"); 118 Validate.isTrue(threadGroupName != null, "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 * Return 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 * Return 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 * Return 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 * Return 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 * Return 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 * Return 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 //if java minimal version for lang becomes 1.8 extend this interface from java.util.function.Predicate 243 public interface ThreadPredicate /*extends java.util.function.Predicate<Thread>*/{ 244 245 /** 246 * Evaluates this predicate on the given thread. 247 * @param thread the thread 248 * @return {@code true} if the thread matches the predicate, otherwise {@code false} 249 */ 250 boolean test(Thread thread); 251 } 252 253 /** 254 * A predicate for selecting threadgroups. 255 */ 256 //if java minimal version for lang becomes 1.8 extend this interface from java.util.function.Predicate 257 public interface ThreadGroupPredicate /*extends java.util.function.Predicate<ThreadGroup>*/{ 258 259 /** 260 * Evaluates this predicate on the given threadgroup. 261 * @param threadGroup the threadgroup 262 * @return {@code true} if the threadGroup matches the predicate, otherwise {@code false} 263 */ 264 boolean test(ThreadGroup threadGroup); 265 } 266 267 /** 268 * Predicate which always returns true. 269 */ 270 public static final AlwaysTruePredicate ALWAYS_TRUE_PREDICATE = new AlwaysTruePredicate(); 271 272 /** 273 * A predicate implementation which always returns true. 274 */ 275 private static final class AlwaysTruePredicate implements ThreadPredicate, ThreadGroupPredicate{ 276 277 private AlwaysTruePredicate() { 278 } 279 280 @Override 281 public boolean test(final ThreadGroup threadGroup) { 282 return true; 283 } 284 285 @Override 286 public boolean test(final Thread thread) { 287 return true; 288 } 289 } 290 291 /** 292 * A predicate implementation which matches a thread or threadgroup name. 293 */ 294 public static class NamePredicate implements ThreadPredicate, ThreadGroupPredicate { 295 296 private final String name; 297 298 /** 299 * Predicate constructor 300 * 301 * @param name thread or threadgroup name 302 * @throws IllegalArgumentException if the name is {@code null} 303 */ 304 public NamePredicate(final String name) { 305 super(); 306 Validate.isTrue(name != null, "The name must not be null"); 307 this.name = name; 308 } 309 310 @Override 311 public boolean test(final ThreadGroup threadGroup) { 312 return threadGroup != null && threadGroup.getName().equals(name); 313 } 314 315 @Override 316 public boolean test(final Thread thread) { 317 return thread != null && thread.getName().equals(name); 318 } 319 } 320 321 /** 322 * A predicate implementation which matches a thread id. 323 */ 324 public static class ThreadIdPredicate implements ThreadPredicate { 325 326 private final long threadId; 327 328 /** 329 * Predicate constructor 330 * 331 * @param threadId the threadId to match 332 * @throws IllegalArgumentException if the threadId is zero or negative 333 */ 334 public ThreadIdPredicate(final long threadId) { 335 super(); 336 if (threadId <= 0) { 337 throw new IllegalArgumentException("The thread id must be greater than zero"); 338 } 339 this.threadId = threadId; 340 } 341 342 @Override 343 public boolean test(final Thread thread) { 344 return thread != null && thread.getId() == threadId; 345 } 346 } 347 348 /** 349 * Select all active threads which match the given predicate. 350 * 351 * @param predicate the predicate 352 * @return An unmodifiable {@code Collection} of active threads matching the given predicate 353 * 354 * @throws IllegalArgumentException if the predicate is null 355 * @throws SecurityException 356 * if the current thread cannot access the system thread group 357 * @throws SecurityException if the current thread cannot modify 358 * thread groups from this thread's thread group up to the system thread group 359 */ 360 public static Collection<Thread> findThreads(final ThreadPredicate predicate){ 361 return findThreads(getSystemThreadGroup(), true, predicate); 362 } 363 364 /** 365 * Select all active threadgroups which match the given predicate. 366 * 367 * @param predicate the predicate 368 * @return An unmodifiable {@code Collection} of active threadgroups matching the given predicate 369 * @throws IllegalArgumentException if the predicate is null 370 * @throws SecurityException 371 * if the current thread cannot access the system thread group 372 * @throws SecurityException if the current thread cannot modify 373 * thread groups from this thread's thread group up to the system thread group 374 */ 375 public static Collection<ThreadGroup> findThreadGroups(final ThreadGroupPredicate predicate){ 376 return findThreadGroups(getSystemThreadGroup(), true, predicate); 377 } 378 379 /** 380 * Select all active threads which match the given predicate and which belongs to the given thread group (or one of its subgroups). 381 * 382 * @param group the thread group 383 * @param recurse if {@code true} then evaluate the predicate recursively on all threads in all subgroups of the given group 384 * @param predicate the predicate 385 * @return An unmodifiable {@code Collection} of active threads which match the given predicate and which belongs to the given thread group 386 * @throws IllegalArgumentException if the given group or predicate is null 387 * @throws SecurityException if the current thread cannot modify 388 * thread groups from this thread's thread group up to the system thread group 389 */ 390 public static Collection<Thread> findThreads(final ThreadGroup group, final boolean recurse, final ThreadPredicate predicate) { 391 Validate.isTrue(group != null, "The group must not be null"); 392 Validate.isTrue(predicate != null, "The predicate must not be null"); 393 394 int count = group.activeCount(); 395 Thread[] threads; 396 do { 397 threads = new Thread[count + (count / 2) + 1]; //slightly grow the array size 398 count = group.enumerate(threads, recurse); 399 //return value of enumerate() must be strictly less than the array size according to javadoc 400 } while (count >= threads.length); 401 402 final List<Thread> result = new ArrayList<>(count); 403 for (int i = 0; i < count; ++i) { 404 if (predicate.test(threads[i])) { 405 result.add(threads[i]); 406 } 407 } 408 return Collections.unmodifiableCollection(result); 409 } 410 411 /** 412 * Select all active threadgroups which match the given predicate and which is a subgroup of the given thread group (or one of its subgroups). 413 * 414 * @param group the thread group 415 * @param recurse if {@code true} then evaluate the predicate recursively on all threadgroups in all subgroups of the given group 416 * @param predicate the predicate 417 * @return An unmodifiable {@code Collection} of active threadgroups which match the given predicate and which is a subgroup of the given thread group 418 * @throws IllegalArgumentException if the given group or predicate is null 419 * @throws SecurityException if the current thread cannot modify 420 * thread groups from this thread's thread group up to the system thread group 421 */ 422 public static Collection<ThreadGroup> findThreadGroups(final ThreadGroup group, final boolean recurse, final ThreadGroupPredicate predicate){ 423 Validate.isTrue(group != null, "The group must not be null"); 424 Validate.isTrue(predicate != null, "The predicate must not be null"); 425 426 int count = group.activeGroupCount(); 427 ThreadGroup[] threadGroups; 428 do { 429 threadGroups = new ThreadGroup[count + (count / 2) + 1]; //slightly grow the array size 430 count = group.enumerate(threadGroups, recurse); 431 //return value of enumerate() must be strictly less than the array size according to javadoc 432 } while(count >= threadGroups.length); 433 434 final List<ThreadGroup> result = new ArrayList<>(count); 435 for(int i = 0; i < count; ++i) { 436 if(predicate.test(threadGroups[i])) { 437 result.add(threadGroups[i]); 438 } 439 } 440 return Collections.unmodifiableCollection(result); 441 } 442}