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 belong's 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 if (threadGroup == null) { 054 throw new IllegalArgumentException("The thread group must not be null"); 055 } 056 final Thread thread = findThreadById(threadId); 057 if(thread != null && threadGroup.equals(thread.getThreadGroup())) { 058 return thread; 059 } 060 return null; 061 } 062 063 /** 064 * Return the active thread with the specified id if it belong's to a thread group with the specified group name. 065 * 066 * @param threadId The thread id 067 * @param threadGroupName The thread group name 068 * @return The threads which belongs to a thread group with the specified group name and the thread's id match the specified id. 069 * {@code null} is returned if no such thread exists 070 * @throws IllegalArgumentException if the specified id is zero or negative or the group name is null 071 * @throws SecurityException 072 * if the current thread cannot access the system thread group 073 * 074 * @throws SecurityException if the current thread cannot modify 075 * thread groups from this thread's thread group up to the system thread group 076 */ 077 public static Thread findThreadById(final long threadId, final String threadGroupName) { 078 if (threadGroupName == null) { 079 throw new IllegalArgumentException("The thread group name must not be null"); 080 } 081 final Thread thread = findThreadById(threadId); 082 if(thread != null && thread.getThreadGroup() != null && thread.getThreadGroup().getName().equals(threadGroupName)) { 083 return thread; 084 } 085 return null; 086 } 087 088 /** 089 * Return active threads with the specified name if they belong to a specified thread group. 090 * 091 * @param threadName The thread name 092 * @param threadGroup The thread group 093 * @return The threads which belongs to a thread group and the thread's name match the specified name, 094 * An empty collection is returned if no such thread exists. The collection returned is always unmodifiable. 095 * @throws IllegalArgumentException if the specified thread name or group is null 096 * @throws SecurityException 097 * if the current thread cannot access the system thread group 098 * 099 * @throws SecurityException if the current thread cannot modify 100 * thread groups from this thread's thread group up to the system thread group 101 */ 102 public static Collection<Thread> findThreadsByName(final String threadName, final ThreadGroup threadGroup) { 103 return findThreads(threadGroup, false, new NamePredicate(threadName)); 104 } 105 106 /** 107 * Return active threads with the specified name if they belong to a thread group with the specified group name. 108 * 109 * @param threadName The thread name 110 * @param threadGroupName The thread group name 111 * @return The threads which belongs to a thread group with the specified group name and the thread's name match the specified name, 112 * An empty collection is returned if no such thread exists. The collection returned is always unmodifiable. 113 * @throws IllegalArgumentException if the specified thread name or group name is null 114 * @throws SecurityException 115 * if the current thread cannot access the system thread group 116 * 117 * @throws SecurityException if the current thread cannot modify 118 * thread groups from this thread's thread group up to the system thread group 119 */ 120 public static Collection<Thread> findThreadsByName(final String threadName, final String threadGroupName) { 121 if (threadName == null) { 122 throw new IllegalArgumentException("The thread name must not be null"); 123 } 124 if (threadGroupName == null) { 125 throw new IllegalArgumentException("The thread group name must not be null"); 126 } 127 128 final Collection<ThreadGroup> threadGroups = findThreadGroups(new NamePredicate(threadGroupName)); 129 130 if(threadGroups.isEmpty()) { 131 return Collections.emptyList(); 132 } 133 134 final Collection<Thread> result = new ArrayList<Thread>(); 135 final NamePredicate threadNamePredicate = new NamePredicate(threadName); 136 for(final ThreadGroup group : threadGroups) { 137 result.addAll(findThreads(group, false, threadNamePredicate)); 138 } 139 return Collections.unmodifiableCollection(result); 140 } 141 142 /** 143 * Return active thread groups with the specified group name. 144 * 145 * @param threadGroupName The thread group name 146 * @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. 147 * @throws IllegalArgumentException if group name is null 148 * @throws SecurityException 149 * if the current thread cannot access the system thread group 150 * 151 * @throws SecurityException if the current thread cannot modify 152 * thread groups from this thread's thread group up to the system thread group 153 */ 154 public static Collection<ThreadGroup> findThreadGroupsByName(final String threadGroupName) { 155 return findThreadGroups(new NamePredicate(threadGroupName)); 156 } 157 158 /** 159 * Return all active thread groups excluding the system thread group (A thread group is active if it has been not destroyed). 160 * 161 * @return all thread groups excluding the system thread group. The collection returned is always unmodifiable. 162 * @throws SecurityException 163 * if the current thread cannot access the system thread group 164 * 165 * @throws SecurityException if the current thread cannot modify 166 * thread groups from this thread's thread group up to the system thread group 167 */ 168 public static Collection<ThreadGroup> getAllThreadGroups() { 169 return findThreadGroups(ALWAYS_TRUE_PREDICATE); 170 } 171 172 /** 173 * Return the system thread group (sometimes also referred as "root thread group"). 174 * 175 * @return the system thread group 176 * @throws SecurityException if the current thread cannot modify 177 * thread groups from this thread's thread group up to the system thread group 178 */ 179 public static ThreadGroup getSystemThreadGroup() { 180 ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); 181 while(threadGroup.getParent() != null) { 182 threadGroup = threadGroup.getParent(); 183 } 184 return threadGroup; 185 } 186 187 /** 188 * Return all active threads (A thread is active if it has been started and has not yet died). 189 * 190 * @return all active threads. The collection returned is always unmodifiable. 191 * @throws SecurityException 192 * if the current thread cannot access the system thread group 193 * 194 * @throws SecurityException if the current thread cannot modify 195 * thread groups from this thread's thread group up to the system thread group 196 */ 197 public static Collection<Thread> getAllThreads() { 198 return findThreads(ALWAYS_TRUE_PREDICATE); 199 } 200 201 /** 202 * Return active threads with the specified name. 203 * 204 * @param threadName The thread name 205 * @return The threads with the specified name or an empty collection if no such thread exists. The collection returned is always unmodifiable. 206 * @throws IllegalArgumentException if the specified name is null 207 * @throws SecurityException 208 * if the current thread cannot access the system thread group 209 * 210 * @throws SecurityException if the current thread cannot modify 211 * thread groups from this thread's thread group up to the system thread group 212 */ 213 public static Collection<Thread> findThreadsByName(final String threadName) { 214 return findThreads(new NamePredicate(threadName)); 215 } 216 217 /** 218 * Return the active thread with the specified id. 219 * 220 * @param threadId The thread id 221 * @return The thread with the specified id or {@code null} if no such thread exists 222 * @throws IllegalArgumentException if the specified id is zero or negative 223 * @throws SecurityException 224 * if the current thread cannot access the system thread group 225 * 226 * @throws SecurityException if the current thread cannot modify 227 * thread groups from this thread's thread group up to the system thread group 228 */ 229 public static Thread findThreadById(final long threadId) { 230 final Collection<Thread> result = findThreads(new ThreadIdPredicate(threadId)); 231 return result.isEmpty() ? null : result.iterator().next(); 232 } 233 234 /** 235 * <p> 236 * ThreadUtils instances should NOT be constructed in standard programming. Instead, the class should be used as 237 * {@code ThreadUtils.getAllThreads()} 238 * </p> 239 * <p> 240 * This constructor is public to permit tools that require a JavaBean instance to operate. 241 * </p> 242 */ 243 public ThreadUtils() { 244 super(); 245 } 246 247 /** 248 * A predicate for selecting threads. 249 */ 250 //if java minimal version for lang becomes 1.8 extend this interface from java.util.function.Predicate 251 public interface ThreadPredicate /*extends java.util.function.Predicate<Thread>*/{ 252 253 /** 254 * Evaluates this predicate on the given thread. 255 * @param thread the thread 256 * @return {@code true} if the thread matches the predicate, otherwise {@code false} 257 */ 258 boolean test(Thread thread); 259 } 260 261 /** 262 * A predicate for selecting threadgroups. 263 */ 264 //if java minimal version for lang becomes 1.8 extend this interface from java.util.function.Predicate 265 public interface ThreadGroupPredicate /*extends java.util.function.Predicate<ThreadGroup>*/{ 266 267 /** 268 * Evaluates this predicate on the given threadgroup. 269 * @param threadGroup the threadgroup 270 * @return {@code true} if the threadGroup matches the predicate, otherwise {@code false} 271 */ 272 boolean test(ThreadGroup threadGroup); 273 } 274 275 /** 276 * Predicate which always returns true. 277 */ 278 public static final AlwaysTruePredicate ALWAYS_TRUE_PREDICATE = new AlwaysTruePredicate(); 279 280 /** 281 * A predicate implementation which always returns true. 282 */ 283 private final static class AlwaysTruePredicate implements ThreadPredicate, ThreadGroupPredicate{ 284 285 private AlwaysTruePredicate() { 286 } 287 288 @Override 289 public boolean test(final ThreadGroup threadGroup) { 290 return true; 291 } 292 293 @Override 294 public boolean test(final Thread thread) { 295 return true; 296 } 297 } 298 299 /** 300 * A predicate implementation which matches a thread or threadgroup name. 301 */ 302 public static class NamePredicate implements ThreadPredicate, ThreadGroupPredicate { 303 304 private final String name; 305 306 /** 307 * Predicate constructor 308 * 309 * @param name thread or threadgroup name 310 * @throws IllegalArgumentException if the name is {@code null} 311 */ 312 public NamePredicate(final String name) { 313 super(); 314 if (name == null) { 315 throw new IllegalArgumentException("The name must not be null"); 316 } 317 this.name = name; 318 } 319 320 @Override 321 public boolean test(final ThreadGroup threadGroup) { 322 return threadGroup != null && threadGroup.getName().equals(name); 323 } 324 325 @Override 326 public boolean test(final Thread thread) { 327 return thread != null && thread.getName().equals(name); 328 } 329 } 330 331 /** 332 * A predicate implementation which matches a thread id. 333 */ 334 public static class ThreadIdPredicate implements ThreadPredicate { 335 336 private final long threadId; 337 338 /** 339 * Predicate constructor 340 * 341 * @param threadId the threadId to match 342 * @throws IllegalArgumentException if the threadId is zero or negative 343 */ 344 public ThreadIdPredicate(final long threadId) { 345 super(); 346 if (threadId <= 0) { 347 throw new IllegalArgumentException("The thread id must be greater than zero"); 348 } 349 this.threadId = threadId; 350 } 351 352 @Override 353 public boolean test(final Thread thread) { 354 return thread != null && thread.getId() == threadId; 355 } 356 } 357 358 /** 359 * Select all active threads which match the given predicate. 360 * 361 * @param predicate the predicate 362 * @return An unmodifiable {@code Collection} of active threads matching the given predicate 363 * 364 * @throws IllegalArgumentException if the predicate is null 365 * @throws SecurityException 366 * if the current thread cannot access the system thread group 367 * @throws SecurityException if the current thread cannot modify 368 * thread groups from this thread's thread group up to the system thread group 369 */ 370 public static Collection<Thread> findThreads(final ThreadPredicate predicate){ 371 return findThreads(getSystemThreadGroup(), true, predicate); 372 } 373 374 /** 375 * Select all active threadgroups which match the given predicate. 376 * 377 * @param predicate the predicate 378 * @return An unmodifiable {@code Collection} of active threadgroups matching the given predicate 379 * @throws IllegalArgumentException if the predicate is null 380 * @throws SecurityException 381 * if the current thread cannot access the system thread group 382 * @throws SecurityException if the current thread cannot modify 383 * thread groups from this thread's thread group up to the system thread group 384 */ 385 public static Collection<ThreadGroup> findThreadGroups(final ThreadGroupPredicate predicate){ 386 return findThreadGroups(getSystemThreadGroup(), true, predicate); 387 } 388 389 /** 390 * Select all active threads which match the given predicate and which belongs to the given thread group (or one of its subgroups). 391 * 392 * @param group the thread group 393 * @param recurse if {@code true} then evaluate the predicate recursively on all threads in all subgroups of the given group 394 * @param predicate the predicate 395 * @return An unmodifiable {@code Collection} of active threads which match the given predicate and which belongs to the given thread group 396 * @throws IllegalArgumentException if the given group or predicate is null 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<Thread> findThreads(final ThreadGroup group, final boolean recurse, final ThreadPredicate predicate) { 401 if (group == null) { 402 throw new IllegalArgumentException("The group must not be null"); 403 } 404 if (predicate == null) { 405 throw new IllegalArgumentException("The predicate must not be null"); 406 } 407 408 int count = group.activeCount(); 409 Thread[] threads; 410 do { 411 threads = new Thread[count + (count / 2) + 1]; //slightly grow the array size 412 count = group.enumerate(threads, recurse); 413 //return value of enumerate() must be strictly less than the array size according to javadoc 414 } while (count >= threads.length); 415 416 final List<Thread> result = new ArrayList<Thread>(count); 417 for (int i = 0; i < count; ++i) { 418 if (predicate.test(threads[i])) { 419 result.add(threads[i]); 420 } 421 } 422 return Collections.unmodifiableCollection(result); 423 } 424 425 /** 426 * Select all active threadgroups which match the given predicate and which is a subgroup of the given thread group (or one of its subgroups). 427 * 428 * @param group the thread group 429 * @param recurse if {@code true} then evaluate the predicate recursively on all threadgroups in all subgroups of the given group 430 * @param predicate the predicate 431 * @return An unmodifiable {@code Collection} of active threadgroups which match the given predicate and which is a subgroup of the given thread group 432 * @throws IllegalArgumentException if the given group or predicate is null 433 * @throws SecurityException if the current thread cannot modify 434 * thread groups from this thread's thread group up to the system thread group 435 */ 436 public static Collection<ThreadGroup> findThreadGroups(final ThreadGroup group, final boolean recurse, final ThreadGroupPredicate predicate){ 437 if (group == null) { 438 throw new IllegalArgumentException("The group must not be null"); 439 } 440 if (predicate == null) { 441 throw new IllegalArgumentException("The predicate must not be null"); 442 } 443 444 int count = group.activeGroupCount(); 445 ThreadGroup[] threadGroups; 446 do { 447 threadGroups = new ThreadGroup[count + (count / 2) + 1]; //slightly grow the array size 448 count = group.enumerate(threadGroups, recurse); 449 //return value of enumerate() must be strictly less than the array size according to javadoc 450 } while(count >= threadGroups.length); 451 452 final List<ThreadGroup> result = new ArrayList<ThreadGroup>(count); 453 for(int i = 0; i < count; ++i) { 454 if(predicate.test(threadGroups[i])) { 455 result.add(threadGroups[i]); 456 } 457 } 458 return Collections.unmodifiableCollection(result); 459 } 460}