diff --git a/src/main/java/org/apache/commons/lang3/ThreadUtils.java b/src/main/java/org/apache/commons/lang3/ThreadUtils.java
new file mode 100644
index 000000000..602facee9
--- /dev/null
+++ b/src/main/java/org/apache/commons/lang3/ThreadUtils.java
@@ -0,0 +1,459 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ *
+ * Helpers for {@code java.lang.Thread} and {@code java.lang.ThreadGroup}.
+ *
+ *
+ * #ThreadSafe#
+ *
+ *
+ * @see java.lang.Thread
+ * @see java.lang.ThreadGroup
+ * @since 3.5
+ * @version $Id$
+ */
+public class ThreadUtils {
+
+ /**
+ * Return the active thread with the specified id if it belong's to the specified thread group.
+ *
+ * @param threadId The thread id
+ * @param threadGroup The thread group
+ * @return The thread which belongs to a specified thread group and the thread's id match the specified id.
+ * {@code null} is returned if no such thread exists
+ * @throws IllegalArgumentException if the specified id is zero or negative or the group is null
+ * @throws SecurityException
+ * if the current thread cannot access the system thread group
+ *
+ * @throws SecurityException if the current thread cannot modify
+ * thread groups from this thread's thread group up to the system thread group
+ */
+ public static Thread findThreadById(final long threadId, final ThreadGroup threadGroup) {
+ if (threadGroup == null) {
+ throw new IllegalArgumentException("The thread group must not be null");
+ }
+ final Thread thread = findThreadById(threadId);
+ if(thread != null && threadGroup.equals(thread.getThreadGroup())) {
+ return thread;
+ }
+ return null;
+ }
+
+ /**
+ * Return the active thread with the specified id if it belong's to a thread group with the specified group name.
+ *
+ * @param threadId The thread id
+ * @param threadGroupName The thread group name
+ * @return The threads which belongs to a thread group with the specified group name and the thread's id match the specified id.
+ * {@code null} is returned if no such thread exists
+ * @throws IllegalArgumentException if the specified id is zero or negative or the group name is null
+ * @throws SecurityException
+ * if the current thread cannot access the system thread group
+ *
+ * @throws SecurityException if the current thread cannot modify
+ * thread groups from this thread's thread group up to the system thread group
+ */
+ public static Thread findThreadById(final long threadId, final String threadGroupName) {
+ if (threadGroupName == null) {
+ throw new IllegalArgumentException("The thread group name must not be null");
+ }
+ final Thread thread = findThreadById(threadId);
+ if(thread != null && thread.getThreadGroup() != null && thread.getThreadGroup().getName().equals(threadGroupName)) {
+ return thread;
+ }
+ return null;
+ }
+
+ /**
+ * Return active threads with the specified name if they belong to a specified thread group.
+ *
+ * @param threadName The thread name
+ * @param threadGroupName The thread group
+ * @return The threads which belongs to a thread group and the thread's name match the specified name,
+ * An empty collection is returned if no such thread exists. The collection returned is always unmodifiable.
+ * @throws IllegalArgumentException if the specified thread name or group is null
+ * @throws SecurityException
+ * if the current thread cannot access the system thread group
+ *
+ * @throws SecurityException if the current thread cannot modify
+ * thread groups from this thread's thread group up to the system thread group
+ */
+ public static Collection findThreadsByName(final String threadName, final ThreadGroup threadGroup) {
+ return findThreads(threadGroup, false, new NamePredicate(threadName));
+ }
+
+ /**
+ * Return active threads with the specified name if they belong to a thread group with the specified group name.
+ *
+ * @param threadName The thread name
+ * @param threadGroupName The thread group name
+ * @return The threads which belongs to a thread group with the specified group name and the thread's name match the specified name,
+ * An empty collection is returned if no such thread exists. The collection returned is always unmodifiable.
+ * @throws IllegalArgumentException if the specified thread name or group name is null
+ * @throws SecurityException
+ * if the current thread cannot access the system thread group
+ *
+ * @throws SecurityException if the current thread cannot modify
+ * thread groups from this thread's thread group up to the system thread group
+ */
+ public static Collection findThreadsByName(final String threadName, final String threadGroupName) {
+ if (threadName == null) {
+ throw new IllegalArgumentException("The thread name must not be null");
+ }
+ if (threadGroupName == null) {
+ throw new IllegalArgumentException("The thread group name must not be null");
+ }
+
+ final Collection result = new ArrayList();
+ for(final ThreadGroup group : findThreadGroups(new NamePredicate(threadGroupName))) {
+ result.addAll(findThreads(group, false, new NamePredicate(threadName)));
+ }
+ return Collections.unmodifiableCollection(result);
+ }
+
+ /**
+ * Return active thread groups with the specified group name.
+ *
+ * @param threadGroupName The thread group name
+ * @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.
+ * @throws IllegalArgumentException if group name is null
+ * @throws SecurityException
+ * if the current thread cannot access the system thread group
+ *
+ * @throws SecurityException if the current thread cannot modify
+ * thread groups from this thread's thread group up to the system thread group
+ */
+ public static Collection findThreadGroupsByName(final String threadGroupName) {
+ return findThreadGroups(new NamePredicate(threadGroupName));
+ }
+
+ /**
+ * Return all active thread groups excluding the system thread group (A thread group is active if it has been not destroyed).
+ *
+ * @return all thread groups excluding the system thread group. The collection returned is always unmodifiable.
+ * @throws SecurityException
+ * if the current thread cannot access the system thread group
+ *
+ * @throws SecurityException if the current thread cannot modify
+ * thread groups from this thread's thread group up to the system thread group
+ */
+ public static Collection getAllThreadGroups() {
+ return findThreadGroups(ALWAYS_TRUE_PREDICATE);
+ }
+
+ /**
+ * Return the system thread group (sometimes also referred as "root thread group").
+ *
+ * @return the system thread group
+ * @throws SecurityException if the current thread cannot modify
+ * thread groups from this thread's thread group up to the system thread group
+ */
+ public static ThreadGroup getSystemThreadGroup() {
+ ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
+ while(threadGroup.getParent() != null) {
+ threadGroup = threadGroup.getParent();
+ }
+ return threadGroup;
+ }
+
+ /**
+ * Return all active threads (A thread is active if it has been started and has not yet died).
+ *
+ * @return all active threads. The collection returned is always unmodifiable.
+ * @throws SecurityException
+ * if the current thread cannot access the system thread group
+ *
+ * @throws SecurityException if the current thread cannot modify
+ * thread groups from this thread's thread group up to the system thread group
+ */
+ public static Collection getAllThreads() {
+ return findThreads(ALWAYS_TRUE_PREDICATE);
+ }
+
+ /**
+ * Return active threads with the specified name.
+ *
+ * @param threadName The thread name
+ * @return The threads with the specified name or an empty collection if no such thread exists. The collection returned is always unmodifiable.
+ * @throws IllegalArgumentException if the specified name is null
+ * @throws SecurityException
+ * if the current thread cannot access the system thread group
+ *
+ * @throws SecurityException if the current thread cannot modify
+ * thread groups from this thread's thread group up to the system thread group
+ */
+ public static Collection findThreadsByName(final String threadName) {
+ return findThreads(new NamePredicate(threadName));
+ }
+
+ /**
+ * Return the active thread with the specified id.
+ *
+ * @param threadId The thread id
+ * @return The thread with the specified id or {@code null} if no such thread exists
+ * @throws IllegalArgumentException if the specified id is zero or negative
+ * @throws SecurityException
+ * if the current thread cannot access the system thread group
+ *
+ * @throws SecurityException if the current thread cannot modify
+ * thread groups from this thread's thread group up to the system thread group
+ */
+ public static Thread findThreadById(final long threadId) {
+ final Collection result = findThreads(new ThreadIdPredicate(threadId));
+
+ if(!result.iterator().hasNext()) {
+ return null;
+ } else {
+ return result.iterator().next();
+ }
+
+ }
+
+ /**
+ *
+ * ThreadUtils instances should NOT be constructed in standard programming. Instead, the class should be used as
+ * {@code ThreadUtils.getAllThreads()}
+ *
+ *
+ * This constructor is public to permit tools that require a JavaBean instance to operate.
+ *
+ */
+ public ThreadUtils() {
+ super();
+ }
+
+ /**
+ * A predicate for selecting threads.
+ */
+ //if java minimal version for lang becomes 1.8 extend this interface from java.util.function.Predicate
+ public static interface ThreadPredicate /*extends java.util.function.Predicate*/{
+
+ /**
+ * Evaluates this predicate on the given thread.
+ * @param thread the thread
+ * @return {@code true} if the thread matches the predicate, otherwise {@code false}
+ */
+ boolean test(Thread thread);
+ }
+
+ /**
+ * A predicate for selecting threadgroups.
+ */
+ //if java minimal version for lang becomes 1.8 extend this interface from java.util.function.Predicate
+ public static interface ThreadGroupPredicate /*extends java.util.function.Predicate*/{
+
+ /**
+ * Evaluates this predicate on the given threadgroup.
+ * @param threadGroup the threadgroup
+ * @return {@code true} if the threadGroup matches the predicate, otherwise {@code false}
+ */
+ boolean test(ThreadGroup threadGroup);
+ }
+
+ /**
+ * Predicate which always returns true.
+ */
+ public static final AlwaysTruePredicate ALWAYS_TRUE_PREDICATE = new AlwaysTruePredicate();
+
+ /**
+ * A predicate implementation which always returns true.
+ */
+ private final static class AlwaysTruePredicate implements ThreadPredicate, ThreadGroupPredicate{
+
+ private AlwaysTruePredicate() {
+ }
+
+ @Override
+ public boolean test(@SuppressWarnings("unused") final ThreadGroup threadGroup) {
+ return true;
+ }
+
+ @Override
+ public boolean test(@SuppressWarnings("unused") final Thread thread) {
+ return true;
+ }
+
+ }
+
+ /**
+ * A predicate implementation which matches a thread or threadgroup name.
+ */
+ public static class NamePredicate implements ThreadPredicate, ThreadGroupPredicate {
+
+ private final String name;
+
+ /**
+ * Predicate constructor
+ *
+ * @param name thread or threadgroup name
+ * @throws IllegalArgumentException if the name is {@code null}
+ */
+ public NamePredicate(final String name) {
+ super();
+ if (name == null) {
+ throw new IllegalArgumentException("The name must not be null");
+ }
+ this.name = name;
+ }
+
+ @Override
+ public boolean test(final ThreadGroup threadGroup) {
+ return threadGroup != null && threadGroup.getName().equals(name);
+ }
+
+ @Override
+ public boolean test(final Thread thread) {
+ return thread != null && thread.getName().equals(name);
+ }
+ }
+
+ /**
+ * A predicate implementation which matches a thread id.
+ */
+ public static class ThreadIdPredicate implements ThreadPredicate {
+
+ private final long threadId;
+
+ /**
+ * Predicate constructor
+ *
+ * @param threadId the threadId to match
+ * @throws IllegalArgumentException if the threadId is zero or negative
+ */
+ public ThreadIdPredicate(final long threadId) {
+ super();
+ if (threadId <= 0) {
+ throw new IllegalArgumentException("The thread id must be greater than zero");
+ }
+ this.threadId = threadId;
+ }
+
+ @Override
+ public boolean test(final Thread thread) {
+ return thread != null && thread.getId() == threadId;
+ }
+ }
+
+ /**
+ * Select all active threads which match the given predicate.
+ *
+ * @param predicate the predicate
+ * @return An unmodifiable {@code Collection} of active threads matching the given predicate
+ *
+ * @throws IllegalArgumentException if the predicate is null
+ * @throws SecurityException
+ * if the current thread cannot access the system thread group
+ * @throws SecurityException if the current thread cannot modify
+ * thread groups from this thread's thread group up to the system thread group
+ */
+ public static Collection findThreads(final ThreadPredicate predicate){
+ return findThreads(getSystemThreadGroup(), true, predicate);
+ }
+
+ /**
+ * Select all active threadgroups which match the given predicate.
+ *
+ * @param predicate
+ * @return An unmodifiable {@code Collection} of active threadgroups matching the given predicate
+ * @throws IllegalArgumentException if the predicate is null
+ * @throws SecurityException
+ * if the current thread cannot access the system thread group
+ * @throws SecurityException if the current thread cannot modify
+ * thread groups from this thread's thread group up to the system thread group
+ */
+ public static Collection findThreadGroups(final ThreadGroupPredicate predicate){
+ return findThreadGroups(getSystemThreadGroup(), true, predicate);
+ }
+
+ /**
+ * Select all active threads which match the given predicate and which belongs to the given thread group (or one of its subgroups).
+ *
+ * @param group the thread group
+ * @param recurse if {@code true} then evaluate the predicate recursively on all threads in all subgroups of the given group
+ * @param predicate the predicate
+ * @return An unmodifiable {@code Collection} of active threads which match the given predicate and which belongs to the given thread group
+ * @throws IllegalArgumentException if the given group or predicate is null
+ * @throws SecurityException if the current thread cannot modify
+ * thread groups from this thread's thread group up to the system thread group
+ */
+ public static Collection findThreads(final ThreadGroup group, final boolean recurse, final ThreadPredicate predicate) {
+ if (group == null) {
+ throw new IllegalArgumentException("The group must not be null");
+ }
+ if (predicate == null) {
+ throw new IllegalArgumentException("The predicate must not be null");
+ }
+
+ final List result = new ArrayList();
+ int count = group.activeCount();
+ Thread[] threads;
+ do {
+ threads = new Thread[count + (count >> 1) + 1];
+ count = group.enumerate(threads, recurse);
+ } while (count >= threads.length);
+
+ for (int i = 0; i < count; ++i) {
+ if (predicate.test(threads[i])) {
+ result.add(threads[i]);
+ }
+ }
+ return Collections.unmodifiableCollection(result);
+ }
+
+ /**
+ * Select all active threadgroups which match the given predicate and which is a subgroup of the given thread group (or one of its subgroups).
+ *
+ * @param group the thread group
+ * @param recurse if {@code true} then evaluate the predicate recursively on all threadgroups in all subgroups of the given group
+ * @param predicate the predicate
+ * @return An unmodifiable {@code Collection} of active threadgroups which match the given predicate and which is a subgroup of the given thread group
+ * @throws IllegalArgumentException if the given group or predicate is null
+ * @throws SecurityException if the current thread cannot modify
+ * thread groups from this thread's thread group up to the system thread group
+ */
+ public static Collection findThreadGroups(final ThreadGroup group, final boolean recurse, final ThreadGroupPredicate predicate){
+ if (group == null) {
+ throw new IllegalArgumentException("The group must not be null");
+ }
+ if (predicate == null) {
+ throw new IllegalArgumentException("The predicate must not be null");
+ }
+ final List result = new ArrayList();
+ int count = group.activeGroupCount();
+ ThreadGroup[] threadGroups;
+ do {
+ threadGroups = new ThreadGroup[count + (count>>1) + 1];
+ count = group.enumerate(threadGroups, recurse);
+ }
+ while(count >= threadGroups.length);
+
+ for(int i = 0; i 0);
+ }
+
+ @Test
+ public void testAtLeastOneThreadGroupsExists() throws InterruptedException {
+ assertTrue(ThreadUtils.getAllThreadGroups().size() > 0);
+ }
+
+ @Test
+ public void testThreadsSameName() throws InterruptedException {
+ final Thread t1 = new TestThread("thread1_XXOOLL__");
+ final Thread alsot1 = new TestThread("thread1_XXOOLL__");
+
+ try {
+ t1.start();
+ alsot1.start();
+ assertEquals(2, ThreadUtils.findThreadsByName("thread1_XXOOLL__").size());
+ } finally {
+ t1.interrupt();
+ alsot1.interrupt();
+ t1.join();
+ alsot1.join();
+ }
+ }
+
+ @Test
+ public void testThreads() throws InterruptedException {
+ final Thread t1 = new TestThread("thread1_XXOOLL__");
+ final Thread t2 = new TestThread("thread2_XXOOLL__");
+
+ try {
+ t1.start();
+ t2.start();
+ assertEquals(1, ThreadUtils.findThreadsByName("thread2_XXOOLL__").size());
+ } finally {
+ t1.interrupt();
+ t2.interrupt();
+ t1.join();
+ t2.join();
+ }
+ }
+
+ @Test
+ public void testThreadsById() throws InterruptedException {
+ final Thread t1 = new TestThread("thread1_XXOOLL__");
+ final Thread t2 = new TestThread("thread2_XXOOLL__");
+
+ try {
+ t1.start();
+ t2.start();
+ assertEquals(t1.getName(), ThreadUtils.findThreadById(t1.getId()).getName());
+ assertSame(t2, ThreadUtils.findThreadById(t2.getId()));
+ } finally {
+ t1.interrupt();
+ t2.interrupt();
+ t1.join();
+ t2.join();
+ }
+ }
+
+ @Test
+ public void testThreadsByIdWrongGroup() throws InterruptedException {
+ final Thread t1 = new TestThread("thread1_XXOOLL__");
+ final ThreadGroup tg = new ThreadGroup("tg__HHEE22");
+
+ try {
+ t1.start();
+ assertNull(ThreadUtils.findThreadById(t1.getId(), tg));
+ } finally {
+ t1.interrupt();
+ t1.join();
+ tg.destroy();
+ }
+ }
+
+
+ @Test
+ public void testThreadGroups() throws InterruptedException {
+ final ThreadGroup threadGroup = new ThreadGroup("thread_group_DDZZ99__");
+ final Thread t1 = new TestThread(threadGroup, "thread1_XXOOPP__");
+ final Thread t2 = new TestThread(threadGroup, "thread2_XXOOPP__");
+
+ try {
+ t1.start();
+ t2.start();
+ assertEquals(1, ThreadUtils.findThreadsByName("thread1_XXOOPP__").size());
+ assertEquals(1, ThreadUtils.findThreadsByName("thread1_XXOOPP__","thread_group_DDZZ99__").size());
+ assertEquals(1, ThreadUtils.findThreadsByName("thread2_XXOOPP__","thread_group_DDZZ99__").size());
+ assertEquals(0, ThreadUtils.findThreadsByName("thread1_XXOOPP__","non_existent_thread_group_JJHHZZ__").size());
+ assertEquals(0, ThreadUtils.findThreadsByName("non_existent_thread_BBDDWW__","thread_group_DDZZ99__").size());
+ assertEquals(1, ThreadUtils.findThreadGroupsByName("thread_group_DDZZ99__").size());
+ assertEquals(0, ThreadUtils.findThreadGroupsByName("non_existent_thread_group_JJHHZZ__").size());
+ assertNotNull(ThreadUtils.findThreadById(t1.getId(),threadGroup));
+ } finally {
+ t1.interrupt();
+ t2.interrupt();
+ t1.join();
+ t2.join();
+ threadGroup.destroy();
+ }
+ }
+
+ @Test
+ public void testThreadGroupsRef() throws InterruptedException {
+ final ThreadGroup threadGroup = new ThreadGroup("thread_group_DDZZ99__");
+ final ThreadGroup deadThreadGroup = new ThreadGroup("dead_thread_group_MMQQSS__");
+ deadThreadGroup.destroy();
+ final Thread t1 = new TestThread(threadGroup, "thread1_XXOOPP__");
+ final Thread t2 = new TestThread(threadGroup, "thread2_XXOOPP__");
+
+ try {
+ t1.start();
+ t2.start();
+ assertEquals(1, ThreadUtils.findThreadsByName("thread1_XXOOPP__").size());
+ assertEquals(1, ThreadUtils.findThreadsByName("thread1_XXOOPP__",threadGroup).size());
+ assertEquals(1, ThreadUtils.findThreadsByName("thread2_XXOOPP__",threadGroup).size());
+ assertEquals(0, ThreadUtils.findThreadsByName("thread1_XXOOPP__",deadThreadGroup).size());
+ } finally {
+ t1.interrupt();
+ t2.interrupt();
+ t1.join();
+ t2.join();
+ threadGroup.destroy();
+ assertEquals(0, ThreadUtils.findThreadsByName("thread2_XXOOPP__",threadGroup).size());
+ }
+ }
+
+ @Test
+ public void testThreadGroupsById() throws InterruptedException {
+ final ThreadGroup threadGroup = new ThreadGroup("thread_group_DDZZ99__");
+ final Thread t1 = new TestThread(threadGroup, "thread1_XXOOPP__");
+ final Thread t2 = new TestThread(threadGroup, "thread2_XXOOPP__");
+ final long nonExistingId = t1.getId()+t2.getId();
+
+ try {
+ t1.start();
+ t2.start();
+ assertEquals(t1.getName(), ThreadUtils.findThreadById(t1.getId(),"thread_group_DDZZ99__").getName());
+ assertEquals(t2.getName(), ThreadUtils.findThreadById(t2.getId(),"thread_group_DDZZ99__").getName());
+ assertNull(ThreadUtils.findThreadById(nonExistingId,"non_existent_thread_group_JJHHZZ__"));
+ assertNull(ThreadUtils.findThreadById(nonExistingId,"thread_group_DDZZ99__"));
+ } finally {
+ t1.interrupt();
+ t2.interrupt();
+ t1.join();
+ t2.join();
+ threadGroup.destroy();
+ }
+ }
+
+ @Test
+ public void testConstructor() throws InterruptedException {
+ assertNotNull(new ThreadUtils());
+ final Constructor>[] cons = ThreadUtils.class.getDeclaredConstructors();
+ assertEquals(1, cons.length);
+ assertTrue(Modifier.isPublic(cons[0].getModifiers()));
+ assertTrue(Modifier.isPublic(ThreadUtils.class.getModifiers()));
+ assertFalse(Modifier.isFinal(ThreadUtils.class.getModifiers()));
+ }
+
+ @Test
+ public void testComplexThreadGroups() throws Exception {
+ final ThreadGroup threadGroup1 = new ThreadGroup("thread_group_1__");
+ final ThreadGroup threadGroup2 = new ThreadGroup("thread_group_2__");
+ final ThreadGroup threadGroup3 = new ThreadGroup(threadGroup2, "thread_group_3__");
+ final ThreadGroup threadGroup4 = new ThreadGroup(threadGroup2, "thread_group_4__");
+ final ThreadGroup threadGroup5 = new ThreadGroup(threadGroup1, "thread_group_5__");
+ final ThreadGroup threadGroup6 = new ThreadGroup(threadGroup4, "thread_group_6__");
+ final List threadGroups = Arrays.asList(threadGroup1,threadGroup2,threadGroup3,threadGroup4,threadGroup5,threadGroup6);
+
+ final Thread t1 = new TestThread("thread1_X__");
+ final Thread t2 = new TestThread(threadGroup1, "thread2_X__");
+ final Thread t3 = new TestThread(threadGroup2, "thread3_X__");
+ final Thread t4 = new TestThread(threadGroup3, "thread4_X__");
+ final Thread t5 = new TestThread(threadGroup4, "thread5_X__");
+ final Thread t6 = new TestThread(threadGroup5, "thread6_X__");
+ final Thread t7 = new TestThread(threadGroup6, "thread7_X__");
+ final Thread t8 = new TestThread(threadGroup4, "thread8_X__");
+ final Thread t9 = new TestThread(threadGroup6, "thread9_X__");
+ final Thread t10 = new TestThread(threadGroup3, "thread10_X__");
+ final List threads = Arrays.asList(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10);
+
+ try {
+ for (final Iterator iterator = threads.iterator(); iterator.hasNext();) {
+ final Thread thread = (Thread) iterator.next();
+ thread.start();
+ }
+ assertTrue(ThreadUtils.getAllThreadGroups().size() >= 7);
+ assertTrue(ThreadUtils.getAllThreads().size() >= 11);
+ assertTrue(ThreadUtils.findThreads(ThreadUtils.ALWAYS_TRUE_PREDICATE).size() >= 11);
+ assertEquals(1, ThreadUtils.findThreadsByName(t4.getName(), threadGroup3.getName()).size());
+ assertEquals(0, ThreadUtils.findThreadsByName(t4.getName(), threadGroup2.getName()).size());
+
+ }finally {
+ for (final Iterator iterator = threads.iterator(); iterator.hasNext();) {
+ final Thread thread = (Thread) iterator.next();
+ thread.interrupt();
+ thread.join();
+ }
+ for (final Iterator iterator = threadGroups.iterator(); iterator.hasNext();) {
+ final ThreadGroup threadGroup = (ThreadGroup) iterator.next();
+ if(!threadGroup.isDestroyed())
+ threadGroup.destroy();
+ }
+ }
+ }
+
+
+ private static class TestThread extends Thread {
+ private final CountDownLatch latch = new CountDownLatch(1);
+
+ public TestThread(final String name) {
+ super(name);
+ }
+
+ public TestThread(final ThreadGroup group, final String name) {
+ super(group, name);
+ }
+
+ @Override
+ public synchronized void start() {
+ super.start();
+ try {
+ latch.await();
+ } catch (final InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ @Override
+ public void run() {
+ latch.countDown();
+ try {
+ synchronized(this){
+ this.wait();
+ }
+ } catch (final InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+}