Introduction
The ThreadUtils class from the Apache Commons Lang library has helper methods for working with Java threads and ThreadGroups. In this post, let us learn about the ThreadUtils class from the Apache Commons Lang library.
Using Apache Commons Lang
When using Maven, you can import the Commons Lang as:
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
You can replace 3.12.0 with the latest version available from Maven.
For Gradle,
implementation 'org.apache.commons:commons-lang3:3.12.0'
A quick introduction to Threads and ThreadGroups in Java
A thread is a thread of execution in a program and a thread group represents a collection of threads. A thread group can have other threads groups. We can imagine a thread group as a tree in which every thread group except the initial thread group has a parent.
Let us look at a quick example to understand this.
Example of Threads and ThreadGroups
As shown below, we have an infinite running Runnable and we create and start a thread with that Runnable. It will create a new thread and that thread executes the passed runnable.
Runnable runnable = () -> {
while (true);
};
Thread t = new Thread(runnable);
t.start();
System.out.println(t);
printTree(t);
The printTree is a helper method, as shown below. It prints the current ThreadGroup up till the parent which is called system.
private static void printTree(Thread t) {
ThreadGroup threadGroup = t.getThreadGroup();
while (threadGroup != null) {
System.out.println(threadGroup);
threadGroup = threadGroup.getParent();
}
System.out.println();
}
Running the above code, it prints,
Thread[Thread-0,5,main]
java.lang.ThreadGroup[name=main,maxpri=10]
java.lang.ThreadGroup[name=system,maxpri=10]
The default toString of a Thread prints the thread name, its priority and the thread group to which it belongs to. By default, a thread will belong to the main thread group.
Custom thread group
Let us now create a custom ThreadGroup named MyThreadGroup1 and create a new thread belonging to this thread group. By default, the parent of the newly created thread group will be the main thread group.
ThreadGroup myThreadGroup1 = new ThreadGroup("MyThreadGroup1");
Thread t = new Thread(myThreadGroup1, runnable);
t.start();
System.out.println(t);
printTree(t);
This will print,
Thread[Thread-1,5,MyThreadGroup1]
java.lang.ThreadGroup[name=MyThreadGroup1,maxpri=10]
java.lang.ThreadGroup[name=main,maxpri=10]
java.lang.ThreadGroup[name=system,maxpri=10]
As we can see, the thread with name Thread-1 belongs to the thread group MyThreadGroup1. Printing the tree hierarchy of the thread groups, we can see that the main is the parent of MyThreadGroup1 and system is the parent of main thread group.
Finally, let us create another thread group with MyThreadGroup1 as the parent. We create a new thread in this thread group and start it.
ThreadGroup myThreadGroup2 = new ThreadGroup(myThreadGroup1, "MyThreadGroup2");
Thread t = new Thread(myThreadGroup2, runnable);
t.start();
System.out.println(t);
printTree(t);
Thread[Thread-2,5,MyThreadGroup2]
java.lang.ThreadGroup[name=MyThreadGroup2,maxpri=10]
java.lang.ThreadGroup[name=MyThreadGroup1,maxpri=10]
java.lang.ThreadGroup[name=main,maxpri=10]
java.lang.ThreadGroup[name=system,maxpri=10]
The thread Thread-2 is part of the thread group MyThreadGroup2. The hierarchy of thread groups (child to parent) is MyThreadGroup2 -> MyThreadGroup1 -> main -> system
ThreadUtils getAllThreads and getAllThreadGroups
The getAllThreads method returns all active threads.
Collection<Thread> allThreads = ThreadUtils.getAllThreads();
System.out.println(allThreads.size());
allThreads.forEach(System.out::println);
7
Thread[Reference Handler,10,system]
Thread[Finalizer,8,system]
Thread[Signal Dispatcher,9,system]
Thread[Notification Thread,9,system]
Thread[main,5,main]
Thread[Monitor Ctrl-Break,5,main]
Thread[Common-Cleaner,8,InnocuousThreadGroup]
The number of threads you get will vary depending on the OS and other factors. The above is just an example of what I got when running this.
Similarly, the getAllThreadGroups returns all active thread groups. This wouldn’t include the system thread group.
Collection<ThreadGroup> allThreadGroups = ThreadUtils.getAllThreadGroups();
System.out.println(allThreadGroups);
System.out.println(allThreadGroups.size());
When running this, I got two thread groups.
[java.lang.ThreadGroup[name=main,maxpri=10], java.lang.ThreadGroup[name=InnocuousThreadGroup,maxpri=10]]
2
Find Thread By Id
There are three overloaded findThreadById methods.
For this, let us start a thread as shown below. The id of the thread is printed as well.
Thread thread = new Thread(() -> {
while (true) ;
});
long id = thread.getId();
thread.start();
System.out.println(thread); //Thread[Thread-0,5,main]
System.out.println(thread.getId()); //15
The simplest of the findThreadById method finds and returns a Thread when passed an id. While the above thread is still running, when we call findThreadById with id as 15, it will return the thread.
Thread foundThread = ThreadUtils.findThreadById(id);
System.out.println(foundThread); //Thread[Thread-0,5,main]
The second overloaded method takes a thread group name along with the thread id. It finds the active thread with the passed id which belongs to the specified thread group name.
Thread foundThread = ThreadUtils.findThreadById(id, "main");
System.out.println(foundThread); //Thread[Thread-0,5,main]
foundThread = ThreadUtils.findThreadById(id, "system");
System.out.println(foundThread); //null
First, we call findThreadById to find thread with id 15 belonging to the main thread group and it returns the thread. In the second call, we pass the thread group as system and hence it returned a null (as the thread with id 15 belongs to a different thread group).
Finally, we could also pass an instance of ThreadGroup rather than the name of the thread group.
ThreadGroup threadGroup = thread.getThreadGroup();
foundThread = ThreadUtils.findThreadById(id, threadGroup);
System.out.println(foundThread); //Thread[Thread-0,5,main]
Note that the ThreadGroup class doesn’t implement the equals method and hence if we had created a new ThreadGroup object as shown below, it won’t return the thread.
ThreadGroup threadGroup = new ThreadGroup("main");
foundThread = ThreadUtils.findThreadById(id, threadGroup);
System.out.println(foundThread); //null
ThreadUtils findThreads method
The findThreads method takes a Predicate(ThreadPredicate) and returns a Collection<Thread> which matches the passed predicate. The ThreadPredicate looks like,
public interface ThreadPredicate {
boolean test(Thread thread);
}
For this example, let us start two threads T1 and T2 as shown below.
Runnable runnable = () -> {
while (true) ;
};
Thread thread1 = new Thread(runnable, "T1");
Thread thread2 = new Thread(runnable, "T2");
thread1.start();
thread2.start();
Let us create a ThreadPredicate which returns true if the name of the thread is T1. Now, calling findThreads with this will return the T1 thread alone.
ThreadUtils.ThreadPredicate threadPredicate = (thread) -> thread.getName().equals("T1");
Collection<Thread> threads = ThreadUtils.findThreads(threadPredicate);
System.out.println(threads); //[Thread[T1,5,main]]
Recursive search on a ThreadGroup
There is another overloaded findThreads method which takes a ThreadGroup and a boolean flag called recurse along with the ThreadPredicate. The method signature is,
public static Collection<Thread> findThreads(
final ThreadGroup group,
final boolean recurse,
final ThreadPredicate predicate)
It returns all threads which match the given predicate and that which belongs to the passed thread group (or one of its subgroups). If we pass the recurse flag as false, it won’t consider the child thread groups (sub-groups) of the specified thread group. If we pass it as true, then it will consider all the threads belonging to the child thread groups as well.
Let us create two ThreadGroups named group-1 and sub-group-1 where the parent of sub-group-1 is group-1. We then create three threads T1, T2 and T3 where T1 and T2 belong to the thread group group-1 and T3 belongs to the thread group sub-group-1. We start all three threads.
Runnable runnable = () -> {
while (true) ;
};
ThreadGroup threadGroup1 = new ThreadGroup("group-1");
ThreadGroup threadGroup2 = new ThreadGroup(threadGroup1, "sub-group-1");
Thread thread1 = new Thread(threadGroup1, runnable, "T1");
Thread thread2 = new Thread(threadGroup1, runnable, "T2");
Thread thread3 = new Thread(threadGroup2, runnable, "T3");
thread1.start();
thread2.start();
thread3.start();
The ThreadPredicate we use here checks if the threads name is either T2 or T3.
ThreadUtils.ThreadPredicate threadPredicate =
(thread) -> thread.getName().equals("T2") || thread.getName().equals("T3");
Calling findThreads as before, will return the threads T2 and T3.
System.out.println(ThreadUtils.findThreads(threadPredicate)); //[Thread[T2,5,group-1], Thread[T3,5,sub-group-1]]
Let us pass the thread predicate, the thread group (group-1) and recurse flag as false. Now, it will find the matching threads belonging to the thread group group-1 which would only be thread T2 (It wouldn’t return Thread T3 as it belongs to a different thread group).
System.out.println(ThreadUtils.findThreads(threadGroup1, false, threadPredicate)); //[Thread[T2,5,group-1]]
When we pass the recurse flag as true, it will also consider threads belonging to the sub-groups (child group) of the passed thread group and hence it will have thread T3 as well in the result.
System.out.println(ThreadUtils.findThreads(threadGroup1, true, threadPredicate)); //[Thread[T2,5,group-1], Thread[T3,5,sub-group-1]]
Finding Threads By Name
We can use the findThreadsByName to find threads by name. The available overloaded methods are similar to findThreadById seen earlier. For this section’s example, let us create and start two threads, named T1 and T2.
Runnable runnable = () -> {
while (true) ;
};
Thread thread1 = new Thread(runnable, "T1");
Thread thread2 = new Thread(runnable, "T2");
thread1.start();
thread2.start();
Calling findThreadsByName by passing name as “T1” returns the T1 thread.
Collection<Thread> threads = ThreadUtils.findThreadsByName("T1");
System.out.println(threads); //[Thread[T1,5,main]]
We can also pass the name of the thread group. In this case, it returns a thread only if the specified thread belongs to the passed thread group.
In the first call, it returns T1, as it belongs to the main thread group. The second call returns an empty list as T1 is not part of the system thread group.
Collection<Thread> threads = ThreadUtils.findThreadsByName("T1", "main");
System.out.println(threads); //[Thread[T1,5,main]]
threads = ThreadUtils.findThreadsByName("T1", "system");
System.out.println(threads); //[]
Finally, we could pass an instance of ThreadGroup as well.
Collection<Thread> threads = ThreadUtils.findThreadsByName("T1", thread1.getThreadGroup());
System.out.println(threads); //[Thread[T1,5,main]]
// ThreadGroup doesn't implement equals
ThreadGroup threadGroup = new ThreadGroup("main");
threads = ThreadUtils.findThreadsByName("T1", threadGroup);
System.out.println(threads); //[]
Finding ThreadGroups
The findThreadGroups is used to find active thread groups which match the given ThreadGroup predicate. A ThreadGroupPredicate is shown below.
public interface ThreadGroupPredicate {
boolean test(ThreadGroup threadGroup);
}
The available overloads are similar to the findThreads methods seen before.
Let us create three thread groups and three threads as shown below.
Runnable runnable = () -> {
while (true) ;
};
ThreadGroup threadGroup1 = new ThreadGroup("group-1");
ThreadGroup threadGroup2 = new ThreadGroup(threadGroup1, "group-2");
ThreadGroup threadGroup3 = new ThreadGroup(threadGroup2, "group-3");
Thread thread1 = new Thread(threadGroup1, runnable, "T1");
Thread thread2 = new Thread(threadGroup2, runnable, "T2");
Thread thread3 = new Thread(threadGroup3, runnable, "T3");
thread1.start();
thread2.start();
thread3.start();
The ThreadGroupPredicate we use checks if the thread group name contains the substring “group".
ThreadUtils.ThreadGroupPredicate threadGroupPredicate = threadGroup ->
threadGroup.getName().contains("group");
With this, calling findThreadGroups, will return all three thread groups we have created.
System.out.println(ThreadUtils.findThreadGroups(threadGroupPredicate));
Prints,
[java.lang.ThreadGroup[name=group-1,maxpri=10], java.lang.ThreadGroup[name=group-2,maxpri=10], java.lang.ThreadGroup[name=group-3,maxpri=10]]
Recursive search
This overloaded method takes a ThreadGroupPredicate, a thread group and the recurse flag. It returns the thread groups which match the passed predicate and which is a subgroup of the given thread group (or one of its subgroups).
Note: Unlike the findThreads method, it starts its search on the subgroup (i.e., immediate child thread groups of the specified thread group). The recurse flag tells it whether or not to evaluate the predicate recursively on all the subgroups of the immediate subgroups.
Passing the above threadGroupPredicate for thread group (group-1) and recurse as false, it will consider only the thread group group-2 and it matches the predicate and hence it returns it.
System.out.println(ThreadUtils.findThreadGroups(threadGroup1, false, threadGroupPredicate));
Prints,
[java.lang.ThreadGroup[name=group-2,maxpri=10]]
With the recurse flag as true, it will consider the thread groups group-2 and group-3.
System.out.println(ThreadUtils.findThreadGroups(threadGroup1, true, threadGroupPredicate));
The above thus returns,
[java.lang.ThreadGroup[name=group-2,maxpri=10], java.lang.ThreadGroup[name=group-3,maxpri=10]]
Find ThreadGroups By Name
The findThreadGroupsByName method finds active thread groups with the specified group name.
System.out.println(ThreadUtils.findThreadGroupsByName("main")); //[java.lang.ThreadGroup[name=main,maxpri=10]]
ThreadUtils getSystemThreadGroup
There is a helper method to get the system thread group.
System.out.println(ThreadUtils.getSystemThreadGroup());//java.lang.ThreadGroup[name=system,maxpri=10]
Conclusion
This concludes this post on the ThreadUtils from the Apache Commons Lang3. Check out the other apache-commons library utilities.