/* * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.lang; import java.io.PrintStream; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import jdk.internal.misc.VM; /** * A thread group represents a set of threads. In addition, a thread group can * also include other thread groups. The thread groups form a tree in which * every thread group except the initial thread group has a parent. * *
A thread group has a name and maximum priority. The name is specified * when creating the group and cannot be changed. The group's maximum priority * is the maximum priority for threads created in the group. It is initially * inherited from the parent thread group but may be changed using the {@link * #setMaxPriority(int) setMaxPriority} method. * *
A thread group is weakly * reachable from its parent group so that it is eligible for garbage * collection when there are no {@linkplain Thread#isAlive() live} threads in the * group and the thread group is otherwise unreachable. * *
Unless otherwise specified, passing a {@code null} argument to a constructor * or method in this class will cause a {@link NullPointerException} to be thrown. * *
* If the {@code pri} argument is less than * {@link Thread#MIN_PRIORITY} or greater than * {@link Thread#MAX_PRIORITY}, the maximum priority of the group * remains unchanged. *
* Otherwise, the priority of this ThreadGroup object is set to the * smaller of the specified {@code pri} and the maximum permitted * priority of the parent of this thread group. (If this thread group * is the system thread group, which has no parent, then its maximum * priority is simply set to {@code pri}.) Then this method is * called recursively, with {@code pri} as its argument, for * every thread group that belongs to this thread group. * * @param pri the new priority of the thread group. * @see #getMaxPriority */ public final void setMaxPriority(int pri) { if (pri >= Thread.MIN_PRIORITY && pri <= Thread.MAX_PRIORITY) { synchronized (this) { if (parent == null) { maxPriority = pri; } else if (this != Thread.virtualThreadGroup()) { maxPriority = Math.min(pri, parent.maxPriority); } subgroups().forEach(g -> g.setMaxPriority(pri)); } } } /** * Tests if this thread group is either the thread group * argument or one of its ancestor thread groups. * * @param g a thread group, can be {@code null} * @return {@code true} if this thread group is the thread group * argument or one of its ancestor thread groups; * {@code false} otherwise. */ public final boolean parentOf(ThreadGroup g) { for (; g != null ; g = g.parent) { if (g == this) { return true; } } return false; } /** * Does nothing. * * @deprecated This method originally determined if the currently running * thread had permission to modify this thread group. This method was only * useful in conjunction with {@linkplain SecurityManager the Security Manager}, * which is no longer supported. There is no replacement for the Security * Manager or this method. */ @Deprecated(since="17", forRemoval=true) public final void checkAccess() { } /** * Returns an estimate of the number of {@linkplain Thread#isAlive() live} * platform threads in this thread group and its subgroups. Virtual threads * are not included in the estimate. This method recursively iterates over * all subgroups in this thread group. * *
The value returned is only an estimate because the number of * threads may change dynamically while this method traverses internal * data structures, and might be affected by the presence of certain * system threads. This method is intended primarily for debugging * and monitoring purposes. * * @return an estimate of the number of live threads in this thread * group and in any other thread group that has this thread * group as an ancestor */ public int activeCount() { int n = 0; for (Thread thread : Thread.getAllThreads()) { ThreadGroup g = thread.getThreadGroup(); if (parentOf(g)) { n++; } } return n; } /** * Copies into the specified array every {@linkplain Thread#isAlive() live} * platform thread in this thread group and its subgroups. Virtual threads * are not enumerated by this method. * *
An invocation of this method behaves in exactly the same * way as the invocation * *
* {@linkplain #enumerate(Thread[], boolean) enumerate}{@code (list, true)} ** * @param list * an array into which to put the list of threads * * @return the number of threads put into the array */ public int enumerate(Thread[] list) { return enumerate(list, true); } /** * Copies into the specified array every {@linkplain Thread#isAlive() live} * platform thread in this thread group. Virtual threads are not enumerated * by this method. If {@code recurse} is {@code true}, this method recursively * enumerates all subgroups of this thread group and references to every live * platform thread in these subgroups are also included. If the array is too * short to hold all the threads, the extra threads are silently ignored. * *
An application might use the {@linkplain #activeCount activeCount} * method to get an estimate of how big the array should be, however * if the array is too short to hold all the threads, the extra threads * are silently ignored. If it is critical to obtain every live * thread in this thread group, the caller should verify that the returned * int value is strictly less than the length of {@code list}. * *
Due to the inherent race condition in this method, it is recommended * that the method only be used for debugging and monitoring purposes. * * @param list * an array into which to put the list of threads * * @param recurse * if {@code true}, recursively enumerate all subgroups of this * thread group * * @return the number of threads put into the array */ public int enumerate(Thread[] list, boolean recurse) { Objects.requireNonNull(list); int n = 0; if (list.length > 0) { for (Thread thread : Thread.getAllThreads()) { ThreadGroup g = thread.getThreadGroup(); if (g == this || (recurse && parentOf(g))) { list[n++] = thread; if (n == list.length) { // list full break; } } } } return n; } /** * Returns an estimate of the number of groups in this thread group and its * subgroups. Recursively iterates over all subgroups in this thread group. * *
The value returned is only an estimate because the number of * thread groups may change dynamically while this method traverses * internal data structures. This method is intended primarily for * debugging and monitoring purposes. * * @return the number of thread groups with this thread group as * an ancestor */ public int activeGroupCount() { int n = 0; for (ThreadGroup group : synchronizedSubgroups()) { n = n + group.activeGroupCount() + 1; } return n; } /** * Copies into the specified array references to every subgroup in this * thread group and its subgroups. * *
An invocation of this method behaves in exactly the same * way as the invocation * *
* {@linkplain #enumerate(ThreadGroup[], boolean) enumerate}{@code (list, true)} ** * @param list * an array into which to put the list of thread groups * * @return the number of thread groups put into the array */ public int enumerate(ThreadGroup[] list) { return enumerate(list, true); } /** * Copies into the specified array references to every subgroup in this * thread group. If {@code recurse} is {@code true}, this method recursively * enumerates all subgroups of this thread group and references to every * thread group in these subgroups are also included. * *
An application might use the * {@linkplain #activeGroupCount activeGroupCount} method to * get an estimate of how big the array should be, however if the * array is too short to hold all the thread groups, the extra thread * groups are silently ignored. If it is critical to obtain every * subgroup in this thread group, the caller should verify that * the returned int value is strictly less than the length of * {@code list}. * *
Due to the inherent race condition in this method, it is recommended
* that the method only be used for debugging and monitoring purposes.
*
* @param list
* an array into which to put the list of thread groups
*
* @param recurse
* if {@code true}, recursively enumerate all subgroups
*
* @return the number of thread groups put into the array
*/
public int enumerate(ThreadGroup[] list, boolean recurse) {
Objects.requireNonNull(list);
return enumerate(list, 0, recurse);
}
/**
* Add a reference to each subgroup to the given array, starting at
* the given index. Returns the new index.
*/
private int enumerate(ThreadGroup[] list, int i, boolean recurse) {
List
* The {@code uncaughtException} method of
* {@code ThreadGroup} does the following:
*
* Applications can override this method in subclasses of
* {@code ThreadGroup} to provide alternative handling of
* uncaught exceptions.
*
* @param t the thread that is about to exit.
* @param e the uncaught exception.
*/
public void uncaughtException(Thread t, Throwable e) {
if (parent != null) {
parent.uncaughtException(t, e);
} else {
Thread.UncaughtExceptionHandler ueh =
Thread.getDefaultUncaughtExceptionHandler();
if (ueh != null) {
ueh.uncaughtException(t, e);
} else {
System.err.print("Exception in thread \"" + t.getName() + "\" ");
e.printStackTrace(System.err);
}
}
}
/**
* Returns a string representation of this Thread group.
*
* @return a string representation of this thread group.
*/
public String toString() {
return getClass().getName()
+ "[name=" + getName()
+ ",maxpri=" + getMaxPriority()
+ "]";
}
/**
* Add a strongly reachable subgroup.
*/
private void synchronizedAddStrong(ThreadGroup group) {
synchronized (this) {
if (groups == null) {
groups = new ThreadGroup[4];
} else if (groups.length == ngroups) {
groups = Arrays.copyOf(groups, ngroups + 4);
}
groups[ngroups++] = group;
}
}
/**
* Add a weakly reachable subgroup.
*/
private void synchronizedAddWeak(ThreadGroup group) {
synchronized (this) {
if (weaks == null) {
@SuppressWarnings({"unchecked", "rawtypes"})
WeakReference
*
*