Path: blob/master/jcl/src/java.base/share/classes/java/lang/ThreadGroup.java
12513 views
/*[INCLUDE-IF Sidecar18-SE & !OPENJDK_THREAD_SUPPORT]*/1/*******************************************************************************2* Copyright (c) 1998, 2022 IBM Corp. and others3*4* This program and the accompanying materials are made available under5* the terms of the Eclipse Public License 2.0 which accompanies this6* distribution and is available at https://www.eclipse.org/legal/epl-2.0/7* or the Apache License, Version 2.0 which accompanies this distribution and8* is available at https://www.apache.org/licenses/LICENSE-2.0.9*10* This Source Code may also be made available under the following11* Secondary Licenses when the conditions for such availability set12* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU13* General Public License, version 2 with the GNU Classpath14* Exception [1] and GNU General Public License, version 2 with the15* OpenJDK Assembly Exception [2].16*17* [1] https://www.gnu.org/software/classpath/license.html18* [2] http://openjdk.java.net/legal/assembly-exception.html19*20* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception21*******************************************************************************/22package java.lang;2324/**25* ThreadGroups are containers of Threads and ThreadGroups, therefore providing26* a tree-like structure to organize Threads. The root ThreadGroup name is "system"27* and it has no parent ThreadGroup. All other ThreadGroups have exactly one parent28* ThreadGroup. All Threads belong to exactly one ThreadGroup.29*30* @author OTI31* @version initial32*33* @see Thread34* @see SecurityManager35*/36public class ThreadGroup implements Thread.UncaughtExceptionHandler {3738private String name; // Name of this ThreadGroup39private int maxPriority = Thread.MAX_PRIORITY; // Maximum priority for Threads inside this ThreadGroup40ThreadGroup parent; // The ThreadGroup to which this ThreadGroup belongs41/*[PR 93952]*/42int numThreads;43private Thread[] childrenThreads = new Thread[5]; // The Threads this ThreadGroup contains44int numGroups; // The number of children groups45private ThreadGroup[] childrenGroups = new ThreadGroup[3]; // The ThreadGroups this ThreadGroup contains4647/*[PR 106322] - Cannot synchronize on childrenThreads and childrenGroups arrays */48/*[PR 122459] LIR646 - Remove use of generic object for synchronization */49private static final class ChildrenGroupsLock { ChildrenGroupsLock() { super(); } }50// Locked when using the childrenGroups field51private Object childrenGroupsLock = new ChildrenGroupsLock();52/*[PR 122459] LIR646 - Remove use of generic object for synchronization */53private static final class ChildrenThreadsLock { ChildrenThreadsLock() { super(); } }54// Locked when using the childrenThreads field55private Object childrenThreadsLock = new ChildrenThreadsLock();5657private boolean isDaemon; // Whether this ThreadGroup is a daemon ThreadGroup or not58private boolean isDestroyed; // Whether this ThreadGroup has already been destroyed or not59/*[PR CMVC 99507] Do not destroy daemon group if threads have been added but not started */60private int addedNotStartedThreads; // Threads that have been added but not yet started6162/**63* Used by the JVM to create the "system" ThreadGroup. Construct64* a ThreadGroup instance, and assign the name "system".65*/66@SuppressWarnings("unused")67private ThreadGroup() {68name = "system"; //$NON-NLS-1$69}7071/**72* Constructs a new ThreadGroup with the name provided.73* The new ThreadGroup will be child of the ThreadGroup to which74* the <code>Thread.currentThread()</code> belongs.75*76* @param name Name for the ThreadGroup being created77*78* @throws SecurityException if <code>checkAccess()</code> for the parent group fails with a SecurityException79*80* @see java.lang.Thread#currentThread81*/8283public ThreadGroup(String name) {84this(Thread.currentThread().getThreadGroup(), name);85}8687/**88* Constructs a new ThreadGroup with the name provided, as child of89* the ThreadGroup <code>parent</code>90*91* @param parent Parent ThreadGroup92* @param name Name for the ThreadGroup being created93*94* @throws NullPointerException if <code>parent</code> is <code>null</code>95* @throws SecurityException if <code>checkAccess()</code> for the parent group fails with a SecurityException96* @throws IllegalThreadStateException if <code>parent</code> has been destroyed already97*/98public ThreadGroup(ThreadGroup parent, String name) {99super();100if (Thread.currentThread() != null) {101// If parent is null we must throw NullPointerException, but that will be done "for free"102// with the message send below103parent.checkAccess();104}105106this.name = name;107this.setParent(parent);108if (parent != null) {109this.setMaxPriority(parent.getMaxPriority());110if (parent.isDaemon()) this.setDaemon(true);111}112}113114/**115* Initialize the "main" ThreadGroup116*/117/*[PR CMVC 71192] Initialize the "main" thread group without calling checkAccess() */118ThreadGroup(ThreadGroup parent) {119this.name = "main"; //$NON-NLS-1$120this.setParent(parent);121}122123/**124* Returns the number of Threads which are children of125* the receiver, directly or indirectly.126*127* @return Number of children Threads128*/129130public int activeCount() {131/*[PR 115667, CMVC 93001] should only count active threads */132int count = 0;133// Lock this subpart of the tree as we walk134synchronized (childrenThreadsLock) {135for (int i = numThreads; --i >= 0;) {136if (childrenThreads[i].isAlive()) {137count++;138}139}140141}142synchronized ( this.childrenGroupsLock ) { // Lock this subpart of the tree as we walk143for (int i = 0; i < numGroups; i++)144count += this.childrenGroups[i].activeCount();145}146return count;147}148/**149* Returns the number of ThreadGroups which are children of150* the receiver, directly or indirectly.151*152* @return Number of children ThreadGroups153*/154155public int activeGroupCount() {156int count = 0;157synchronized (this.childrenGroupsLock) { // Lock this subpart of the tree as we walk158for (int i = 0 ; i < numGroups; i++)159// One for this group & the subgroups160count += 1 + this.childrenGroups[i].activeGroupCount();161}162return count;163}164165final void checkNewThread(Thread thread) throws IllegalThreadStateException {166synchronized (this.childrenThreadsLock) {167/*[PR 1FJC24P] testing state has to be done inside synchronized */168if (isDestroyed) {169throw new IllegalThreadStateException();170}171addedNotStartedThreads++;172}173}174175/**176* Adds a Thread to the receiver. This should only be visible to class177* java.lang.Thread, and should only be called when a new Thread is created178* and initialized by the constructor.179*180* @param thread Thread to add to the receiver181*182* @throws IllegalThreadStateException if the receiver has been destroyed already183*184* @see #remove(java.lang.Thread)185*/186187final void add(Thread thread) throws IllegalThreadStateException {188synchronized (this.childrenThreadsLock) {189/*[PR 1FJC24P] testing state has to be done inside synchronized */190if (!isDestroyed) {191if (childrenThreads.length == numThreads) {192Thread[] newThreads = new Thread[childrenThreads.length * 2];193System.arraycopy(childrenThreads, 0, newThreads, 0, numThreads);194newThreads[numThreads++] = thread;195childrenThreads = newThreads;196} else childrenThreads[numThreads++] = thread;197addedNotStartedThreads--;198} else throw new IllegalThreadStateException();199}200}201202/**203* Adds a ThreadGroup to the receiver.204*205* @param g ThreadGroup to add to the receiver206*207* @throws IllegalThreadStateException if the receiver has been destroyed already208*209*/210private void add(ThreadGroup g) throws IllegalThreadStateException {211/*[PR 1FJC24P] testing state has to be done inside synchronized */212/*[PR JAZZ 9154] ThreadGroup.isDestroyed is not properly synchronized */213synchronized(this.childrenThreadsLock) {214synchronized (this.childrenGroupsLock) {215if (!isDestroyed()) {216if (childrenGroups.length == numGroups) {217ThreadGroup[] newGroups = new ThreadGroup[childrenGroups.length * 2];218System.arraycopy(childrenGroups, 0, newGroups, 0, numGroups);219childrenGroups = newGroups;220}221childrenGroups[numGroups++] = g;222}223else throw new IllegalThreadStateException();224}225}226}227228/**229* The definition of this method depends on the deprecated method <code>suspend()</code>.230* The behavior of this call was never specified.231*232* @param b Used to control low memory implicit suspension233* @return always returns true234*235* @deprecated Required deprecated method suspend().236*/237/*[IF JAVA_SPEC_VERSION >= 11]*/238/*[IF JAVA_SPEC_VERSION >= 14]*/239@Deprecated(forRemoval=true, since="1.2")240/*[ELSE] JAVA_SPEC_VERSION >= 14 */241@Deprecated(forRemoval=false, since="1.2")242/*[ENDIF] JAVA_SPEC_VERSION >= 14 */243/*[ELSE] JAVA_SPEC_VERSION >= 11 */244@Deprecated245/*[ENDIF] JAVA_SPEC_VERSION >= 11 */246public boolean allowThreadSuspension(boolean b) {247// Does not apply to this VM, no-op248/*[PR 1PR4U1E]*/249return true;250}251/**252* If there is a SecurityManager installed, call <code>checkAccess</code>253* in it passing the receiver as parameter, otherwise do nothing.254*/255/*[IF JAVA_SPEC_VERSION >= 17]*/256@Deprecated(since="17", forRemoval=true)257/*[ENDIF] JAVA_SPEC_VERSION >= 17 */258public final void checkAccess() {259// Forwards the message to the SecurityManager (if there's one)260// passing the receiver as parameter261@SuppressWarnings("removal")262SecurityManager currentManager = System.getSecurityManager();263if (currentManager != null) currentManager.checkAccess(this);264}265/**266* Destroys the receiver and recursively all its subgroups. It is only legal267* to destroy a ThreadGroup that has no Threads.268* Any daemon ThreadGroup is destroyed automatically when it becomes empty269* (no Threads and no ThreadGroups in it).270*271* @throws IllegalThreadStateException if the receiver or any of its subgroups has been destroyed already272* @throws SecurityException if <code>this.checkAccess()</code> fails with a SecurityException273/*[IF JAVA_SPEC_VERSION >= 16]274* @deprecated275/*[ENDIF] JAVA_SPEC_VERSION >= 16276*/277/*[IF JAVA_SPEC_VERSION >= 16]*/278@Deprecated(forRemoval = true, since = "16")279/*[ENDIF] JAVA_SPEC_VERSION >= 16 */280public final void destroy() {281destroyImpl();282removeFromParent();283}284285/**286* Do the destroy logic but do not remove ourselves from the parent threadgroup287* Callers must be sure to call removeFromParent() without holding locks to avoid deadlocks288*/289private void destroyImpl(){290/*[PR CMVC 198585] Deadlock when removing self from parent */291checkAccess();292293synchronized (this.childrenThreadsLock) { // Lock this subpart of the tree as we walk294synchronized (this.childrenGroupsLock) {295/*[PR 1FJC24P] testing state has to be done inside synchronized */296if (isDestroyed)297/*[MSG "K0056", "Already destroyed"]*/298throw new IllegalThreadStateException(com.ibm.oti.util.Msg.getString("K0056")); //$NON-NLS-1$299if (numThreads > 0)300/*[MSG "K0057", "Has threads"]*/301throw new IllegalThreadStateException(com.ibm.oti.util.Msg.getString("K0057")); //$NON-NLS-1$302303int toDestroy = numGroups;304// Call recursively for subgroups305for (int i = 0 ; i < toDestroy; i++) {306// We always get the first element - remember,307// when the child dies it removes itself from our collection. See removeFromParent().308this.childrenGroups[0].destroy();309}310/*[PR CMVC 137999] move enclosing braces to avoid deadlock (allow parent to be removed outside of locks) */311}312// Now that the ThreadGroup is really destroyed it can be tagged as so313/*[PR 1FJJ0N7] Comment and code have to be consistent */314this.isDestroyed = true;315}316317}318/**319* Auxiliary method that destroys the receiver and recursively all its subgroups320* if the receiver is a daemon ThreadGroup.321*322* @see #destroy323* @see #setDaemon324* @see #isDaemon325*/326327private void destroyIfEmptyDaemon() {328boolean shouldRemoveFromParent = false;329330// Has to be non-destroyed daemon to make sense331synchronized (this.childrenThreadsLock) {332/*[PR 1FJC24P] testing state has to be done inside synchronized */333if (isDaemon && !isDestroyed &&334/*[PR CMVC 99507] Do not destroy daemon group if threads have been added but not started */335addedNotStartedThreads == 0 &&336numThreads == 0)337{338synchronized (this.childrenGroupsLock) {339if (numGroups == 0) {340destroyImpl();341shouldRemoveFromParent = true;342}343}344}345}346347if (shouldRemoveFromParent) {348removeFromParent();349}350}351352/**353* Does a nullcheck and removes this threadgroup from the parent.354* Callers must not hold any locks when calling this function or a deadlock my occur355*/356private void removeFromParent() {357/*[PR CMVC 198585] Deadlock when removing self from parent */358/*[PR 97314] Cannot call getParent() */359if (parent != null) {360parent.remove(this);361}362}363/**364* Copies an array with all Threads which are children of365* the receiver (directly or indirectly) into the array <code>threads</code>366* passed as parameters. If the array passed as parameter is too small no367* exception is thrown - the extra elements are simply not copied.368*369* @param threads Thread array into which the Threads will be copied370* @return How many Threads were copied over371*372*/373374public int enumerate(Thread[] threads) {375return enumerate(threads, true);376}377/**378* Copies an array with all Threads which are children of379* the receiver into the array <code>threads</code>380* passed as parameter. Children Threads of subgroups are recursively copied381* as well if parameter <code>recurse</code> is <code>true</code>.382*383* If the array passed as parameter is too small no384* exception is thrown - the extra elements are simply not copied.385*386* @param threads array into which the Threads will be copied387* @param recurse Indicates whether Threads in subgroups should be recursively copied as well or not388* @return How many Threads were copied over389*390*/391392public int enumerate(Thread[] threads, boolean recurse) {393return enumerateGeneric(threads, recurse, 0, true);394}395/**396* Copies an array with all ThreadGroups which are children of397* the receiver (directly or indirectly) into the array <code>groups</code>398* passed as parameters. If the array passed as parameter is too small no399* exception is thrown - the extra elements are simply not copied.400*401* @param groups array into which the ThreadGroups will be copied402* @return How many ThreadGroups were copied over403*404*/405406public int enumerate(ThreadGroup[] groups) {407return enumerate(groups, true);408}409/**410* Copies an array with all ThreadGroups which are children of411* the receiver into the array <code>groups</code>412* passed as parameter. Children ThreadGroups of subgroups are recursively copied413* as well if parameter <code>recurse</code> is <code>true</code>.414*415* If the array passed as parameter is too small no416* exception is thrown - the extra elements are simply not copied.417*418* @param groups array into which the ThreadGroups will be copied419* @param recurse Indicates whether ThreadGroups in subgroups should be recursively copied as well or not420* @return How many ThreadGroups were copied over421*422*/423424public int enumerate(ThreadGroup[] groups, boolean recurse) {425return enumerateGeneric(groups, recurse, 0, false);426}427/**428* Copies into <param>enumeration</param> starting at </param>enumerationIndex</param>429* all Threads or ThreadGroups in the receiver. If </param>recurse</param>430* is true, recursively enumerate the elements in subgroups.431*432* If the array passed as parameter is too small no433* exception is thrown - the extra elements are simply not copied.434*435* @param enumeration array into which the elements will be copied436* @param recurse Indicates whether </param>recurseCollection</param> should be enumerated or not437* @param enumerationIndex Indicates in which position of the enumeration array we are438* @param enumeratingThreads Indicates whether we are enumerating Threads or ThreadGroups439* @return How many elements were enumerated/copied over440*/441442private int enumerateGeneric(Object[] enumeration, boolean recurse, int enumerationIndex, boolean enumeratingThreads) {443checkAccess();444445Object syncLock = enumeratingThreads ? childrenThreadsLock : childrenGroupsLock;446447synchronized (syncLock) { // Lock this subpart of the tree as we walk448/*[PR CMVC 94112] ArrayIndexOutOfBoundsException when enumerating Threads.*/449Object[] immediateCollection = enumeratingThreads ? (Object[])childrenThreads : (Object[])childrenGroups;450451for (int i = enumeratingThreads ? numThreads : numGroups; --i >= 0;) {452if (!enumeratingThreads || ((Thread)immediateCollection[i]).isAlive()) {453if (enumerationIndex >= enumeration.length) return enumerationIndex;454enumeration[enumerationIndex++] = immediateCollection[i];455}456}457}458459if (recurse) { // Lock this subpart of the tree as we walk460synchronized (this.childrenGroupsLock) {461for (int i = 0; i < numGroups; i++) {462if (enumerationIndex >= enumeration.length) return enumerationIndex;463enumerationIndex = childrenGroups[i].464enumerateGeneric(enumeration, recurse, enumerationIndex, enumeratingThreads);465}466}467}468return enumerationIndex;469}470471/**472* Copies into <param>enumeration</param> starting at </param>enumerationIndex</param>473* all Threads or ThreadGroups in the receiver. If </param>recurse</param>474* is true, recursively enumerate the elements in subgroups.475*476* If the array passed as parameter is too small no477* exception is thrown - the extra elements are simply not copied.478*479* @param enumeration array into which the elements will be copied480* @param recurse Indicates whether </param>recurseCollection</param> should be enumerated or not481* @param enumerationIndex Indicates in which position of the enumeration array we are482* @param enumeratingThreads Indicates whether we are enumerating Threads or ThreadGroups483* @return How many elements were enumerated/copied over484*/485486int enumerateDeadThreads(Object[] enumeration, int enumerationIndex) {487boolean recurse = true;488boolean enumeratingThreads = true;489490Object syncLock = enumeratingThreads ? childrenThreadsLock : childrenGroupsLock;491492synchronized (syncLock) { // Lock this subpart of the tree as we walk493/*[PR CMVC 94112] ArrayIndexOutOfBoundsException when enumerating Threads.*/494Object[] immediateCollection = enumeratingThreads ? (Object[])childrenThreads : (Object[])childrenGroups;495496for (int i = enumeratingThreads ? numThreads : numGroups; --i >= 0;) {497if (!enumeratingThreads || !((Thread)immediateCollection[i]).isAlive()) {498if (enumerationIndex >= enumeration.length) return enumerationIndex;499enumeration[enumerationIndex++] = immediateCollection[i];500}501}502}503504if (recurse) { // Lock this subpart of the tree as we walk505synchronized (this.childrenGroupsLock) {506for (int i = 0; i < numGroups; i++) {507if (enumerationIndex >= enumeration.length) return enumerationIndex;508enumerationIndex = childrenGroups[i].509enumerateDeadThreads(enumeration, enumerationIndex);510}511}512}513return enumerationIndex;514}515516/**517* Answers the maximum allowed priority for a Thread in the receiver.518*519* @return the maximum priority (an <code>int</code>)520*521* @see #setMaxPriority522*/523524public final int getMaxPriority() {525return maxPriority;526}527/**528* Answers the name of the receiver.529*530* @return the receiver's name (a java.lang.String)531*/532533public final String getName() {534return name;535}536/**537* Answers the receiver's parent ThreadGroup. It can be null if the receiver538* is the root ThreadGroup.539*540* @return the parent ThreadGroup541*542*/543544public final ThreadGroup getParent() {545/*[PR 97314]*/546if (parent != null)547parent.checkAccess();548else {549/*[IF] user created threadgroups can set the name to be system, however in550* the test below the name hasn't been set yet if the parent is null, and a551* nullpointerexception was thrown during threadgroup creation. */552/*[ENDIF]*/553/*[MSG "K0550", "current thread cannot modify this thread group"]*/554if (this.name == null || !this.name.equalsIgnoreCase("system")) //$NON-NLS-1$555throw new SecurityException(com.ibm.oti.util.Msg.getString("K0550")); //$NON-NLS-1$556}557558return parent;559}560/**561* Interrupts every Thread in the receiver and recursively in all its subgroups.562*563* @throws SecurityException if <code>this.checkAccess()</code> fails with a SecurityException564*565* @see Thread#interrupt566*/567568public final void interrupt() {569checkAccess();570synchronized (this.childrenThreadsLock) { // Lock this subpart of the tree as we walk571for (int i = 0 ; i < numThreads; i++)572this.childrenThreads[i].interrupt();573}574synchronized (this.childrenGroupsLock) { // Lock this subpart of the tree as we walk575for (int i = 0 ; i < numGroups; i++)576this.childrenGroups[i].interrupt();577}578}579/**580* Answers true if the receiver is a daemon ThreadGroup, false otherwise.581*582* @return if the receiver is a daemon ThreadGroup583*584* @see #setDaemon585* @see #destroy586/*[IF JAVA_SPEC_VERSION >= 16]587* @deprecated588/*[ENDIF] JAVA_SPEC_VERSION >= 16589*/590/*[IF JAVA_SPEC_VERSION >= 16]*/591@Deprecated(forRemoval = true, since = "16")592/*[ENDIF] JAVA_SPEC_VERSION >= 16 */593public final boolean isDaemon() {594return isDaemon;595}596597/**598* Answers true if the receiver has been destroyed already, false otherwise.599*600* @return if the receiver has been destroyed already601*602* @see #destroy603/*[IF JAVA_SPEC_VERSION >= 16]604* @deprecated605/*[ENDIF] JAVA_SPEC_VERSION >= 16606*/607/*[IF JAVA_SPEC_VERSION >= 16]*/608@Deprecated(forRemoval = true, since = "16")609/*[ENDIF] JAVA_SPEC_VERSION >= 16 */610public boolean isDestroyed() {611// never call this when synchronized on childrenThreadsGroup or deadlock will occur612/*[PR JAZZ 9154] ThreadGroup.isDestroyed is not properly synchronized */613synchronized(childrenThreadsLock) {614return isDestroyed;615}616}617/**618* Outputs to <code>System.out</code> a text representation of the hierarchy of619* Threads and ThreadGroups in the receiver (and recursively). Proper indentation620* is done to suggest the nesting of groups inside groups and threads inside groups.621*/622623public void list() {624System.out.println(); // We start in a fresh line625list(0);626}627/**628* Outputs to <code>System.out</code>a text representation of the hierarchy of629* Threads and ThreadGroups in the receiver (and recursively). The indentation630* will be four spaces per level of nesting.631*632* @param levels How many levels of nesting, so that proper indentation can be output.633*634*/635636private void list(int levels) {637String spaces = " "; // 4 spaces for each level //$NON-NLS-1$638for (int i = 0; i < levels; i++)639System.out.print(spaces);640641// Print the receiver642System.out.println(this.toString());643644// Print the children threads, with 1 extra indentation645synchronized (this.childrenThreadsLock) {646for (int i = 0; i < numThreads; i++) {647for (int j = 0; j <= levels; j++)648System.out.print(spaces); // children get an extra indentation, 4 spaces for each level649System.out.println(this.childrenThreads[i]);650}651}652synchronized (this.childrenGroupsLock) {653for (int i = 0; i < numGroups; i++)654this.childrenGroups[i].list(levels + 1);655}656}657/**658* Answers true if the receiver is a direct or indirect parent group of659* ThreadGroup <code>g</code>, false otherwise.660*661* @param g ThreadGroup to test662*663* @return if the receiver is parent of the ThreadGroup passed as parameter664*665*/666667public final boolean parentOf(ThreadGroup g) {668while (g != null) {669if (this == g) return true;670/*[PR 97314] Cannot call getParent() */671g = g.parent;672}673return false;674}675676/**677* Removes a Thread from the receiver. This should only be visible to class678* java.lang.Thread, and should only be called when a Thread dies.679*680* @param thread Thread to remove from the receiver681*682* @see #add(Thread)683*/684final void remove(java.lang.Thread thread) {685686/*[PR JAZZ 86608] Hang because ThreadGroup.remove synchronizes childrenThreadsLock and then synchronizes ThreadGroup itself */687boolean isThreadGroupEmpty = false;688689synchronized (this.childrenThreadsLock) {690for (int i=0; i<numThreads; i++) {691/*[PR CMVC 109438] Dead Threads not removed from ThreadGroups */692if (childrenThreads[i] == thread) {693numThreads--;694if (numThreads == 0) {695isThreadGroupEmpty = true;696} else {697System.arraycopy (childrenThreads, i + 1, childrenThreads, i, numThreads - i);698}699childrenThreads[numThreads] = null;700break;701}702}703}704705/*[PR CMVC 114880] ThreadGroup is not notified when all threads complete */706if (isThreadGroupEmpty) {707synchronized (this) {708notifyAll();709}710}711destroyIfEmptyDaemon();712}713714/**715* Removes an immediate subgroup from the receiver.716*717* @param g Threadgroup to remove from the receiver718*719* @see #add(Thread)720* @see #add(ThreadGroup)721*/722private void remove(ThreadGroup g) {723synchronized (this.childrenGroupsLock) {724for (int i=0; i<numGroups; i++) {725/*[PR CMVC 109438] Dead Threads not removed from ThreadGroups */726if (childrenGroups[i] == g) {727numGroups--;728System.arraycopy (childrenGroups, i + 1, childrenGroups, i, numGroups - i);729childrenGroups[numGroups] = null;730break;731}732}733}734destroyIfEmptyDaemon();735}736/**737* Resumes every Thread in the receiver and recursively in all its subgroups.738*739* @throws SecurityException if <code>this.checkAccess()</code> fails with a SecurityException740*741* @see Thread#resume742* @see #suspend743*744* @deprecated Requires deprecated method Thread.resume().745*/746/*[IF JAVA_SPEC_VERSION >= 11]*/747/*[IF JAVA_SPEC_VERSION >= 14]*/748@Deprecated(forRemoval=true, since="1.2")749/*[ELSE] JAVA_SPEC_VERSION >= 14 */750@Deprecated(forRemoval=false, since="1.2")751/*[ENDIF] JAVA_SPEC_VERSION >= 14 */752/*[ELSE] JAVA_SPEC_VERSION >= 11 */753@Deprecated754/*[ENDIF] JAVA_SPEC_VERSION >= 11 */755public final void resume() {756checkAccess();757synchronized (this.childrenThreadsLock) { // Lock this subpart of the tree as we walk758for (int i = 0 ; i < numThreads; i++)759this.childrenThreads[i].resume();760}761synchronized (this.childrenGroupsLock) { // Lock this subpart of the tree as we walk762for (int i = 0 ; i < numGroups; i++)763this.childrenGroups[i].resume();764}765}766/**767* Configures the receiver to be a daemon ThreadGroup or not.768* Daemon ThreadGroups are automatically destroyed when they become empty.769*770* @param isDaemon new value defining if receiver should be daemon or not771*772* @throws SecurityException if <code>checkAccess()</code> for the parent group fails with a SecurityException773*774* @see #isDaemon775* @see #destroy776/*[IF JAVA_SPEC_VERSION >= 16]777* @deprecated778/*[ENDIF] JAVA_SPEC_VERSION >= 16779*/780/*[IF JAVA_SPEC_VERSION >= 16]*/781@Deprecated(forRemoval = true, since = "16")782/*[ENDIF] JAVA_SPEC_VERSION >= 16 */783public final void setDaemon(boolean isDaemon) {784checkAccess();785this.isDaemon = isDaemon;786}787/**788* Configures the maximum allowed priority for a Thread in the receiver789* and recursively in all its subgroups.790*791* One can never change the maximum priority of a ThreadGroup to be792* higher than it was. Such an attempt will not result in an exception, it will793* simply leave the ThreadGroup with its current maximum priority.794*795* @param newMax the new maximum priority to be set796*797* @throws SecurityException if <code>checkAccess()</code> fails with a SecurityException798* @throws IllegalArgumentException if the new priority is greater than Thread.MAX_PRIORITY or less than799* Thread.MIN_PRIORITY800*801* @see #getMaxPriority802*/803804public final void setMaxPriority(int newMax) {805checkAccess();806807/*[PR 1FJ9S51] If new priority is greater than the current maximum, the maximum remains unchanged */808/*[PR CMVC 177870] Java7:JCK:java_lang/ThreadGroup/setMaxPriority fails in all platform */809if (Thread.MIN_PRIORITY <= newMax && newMax <= this.maxPriority) {810/*[PR 97314] Cannot call getParent() */811int parentPriority = parent == null ? newMax : parent.getMaxPriority();812this.maxPriority = parentPriority <= newMax ? parentPriority : newMax;813synchronized (this.childrenGroupsLock) { // Lock this subpart of the tree as we walk814for (int i = 0 ; i < numGroups; i++)815this.childrenGroups[i].setMaxPriority(newMax);816}817}818}819/**820* Sets the parent ThreadGroup of the receiver, and adds the receiver to the parent's821* collection of immediate children (if <code>parent</code> is not <code>null</code>).822*823* @param parent The parent ThreadGroup, or null if the receiver is to be the root ThreadGroup824*825* @see #getParent826* @see #parentOf827*/828829private void setParent(ThreadGroup parent) {830if (parent != null) parent.add(this);831this.parent = parent;832}833/**834* Stops every Thread in the receiver and recursively in all its subgroups.835*836* @throws SecurityException if <code>this.checkAccess()</code> fails with a SecurityException837*838* @see Thread#stop()839/*[IF JAVA_SPEC_VERSION < 11]840* @see Thread#stop(Throwable)841/*[ENDIF] JAVA_SPEC_VERSION < 11842* @see ThreadDeath843*844* @deprecated Requires deprecated method Thread.stop().845*/846/*[IF JAVA_SPEC_VERSION >= 16]*/847@Deprecated(forRemoval = true, since = "1.2")848/*[ELSE]*/849/*[IF Sidecar19-SE]*/850@Deprecated(forRemoval = false, since = "1.2")851/*[ELSE] Sidecar19-SE */852@Deprecated853/*[ENDIF] Sidecar19-SE */854/*[ENDIF] JAVA_SPEC_VERSION >= 16 */855public final void stop() {856/*[PR CMVC 73122] Stop the running thread last */857if (stopHelper())858Thread.currentThread().stop();859}860861/**862* @deprecated Requires deprecated method Thread.suspend().863*/864@Deprecated865private final boolean stopHelper() {866checkAccess();867868boolean stopCurrent = false;869synchronized (this.childrenThreadsLock) { // Lock this subpart of the tree as we walk870Thread current = Thread.currentThread();871for (int i = 0 ; i < numThreads; i++)872if (this.childrenThreads[i] == current) {873stopCurrent = true;874} else {875this.childrenThreads[i].stop();876}877}878synchronized (this.childrenGroupsLock) { // Lock this subpart of the tree as we walk879for (int i = 0 ; i < numGroups; i++)880stopCurrent |= this.childrenGroups[i].stopHelper();881}882return stopCurrent;883}884/**885* Suspends every Thread in the receiver and recursively in all its subgroups.886*887* @throws SecurityException if <code>this.checkAccess()</code> fails with a SecurityException888*889* @see Thread#suspend890* @see #resume891*892* @deprecated Requires deprecated method Thread.suspend().893*/894/*[IF JAVA_SPEC_VERSION >= 11]*/895/*[IF JAVA_SPEC_VERSION >= 14]*/896@Deprecated(forRemoval=true, since="1.2")897/*[ELSE] JAVA_SPEC_VERSION >= 14 */898@Deprecated(forRemoval=false, since="1.2")899/*[ENDIF] JAVA_SPEC_VERSION >= 14 */900/*[ELSE] JAVA_SPEC_VERSION >= 11 */901@Deprecated902/*[ENDIF] JAVA_SPEC_VERSION >= 11 */903public final void suspend() {904if (suspendHelper())905Thread.currentThread().suspend();906}907908/**909* @deprecated Requires deprecated method Thread.suspend().910*/911@Deprecated912private final boolean suspendHelper() {913checkAccess();914915boolean suspendCurrent = false;916synchronized (this.childrenThreadsLock) { // Lock this subpart of the tree as we walk917Thread current = Thread.currentThread();918for (int i = 0 ; i < numThreads; i++)919if (this.childrenThreads[i] == current) {920suspendCurrent = true;921} else {922this.childrenThreads[i].suspend();923}924}925synchronized (this.childrenGroupsLock) { // Lock this subpart of the tree as we walk926for (int i = 0 ; i < numGroups; i++)927suspendCurrent |= this.childrenGroups[i].suspendHelper();928}929return suspendCurrent;930}931932/**933* Answers a string containing a concise, human-readable934* description of the receiver.935*936* @return a printable representation for the receiver.937*/938@Override939public String toString() {940return getClass().getName() + "[name=" + this.getName() + ",maxpri=" + this.getMaxPriority() + "]" ; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$941}942943944/**945* Any uncaught exception in any Thread has to be forwarded (by the VM) to the Thread's ThreadGroup946* by sending this message (uncaughtException). This allows users to define custom ThreadGroup classes947* and custom behavior for when a Thread has an uncaughtException or when it does (ThreadDeath).948*949* @param t Thread with an uncaught exception950* @param e The uncaught exception itself951*952* @see Thread#stop()953/*[IF JAVA_SPEC_VERSION < 11]954* @see Thread#stop(Throwable)955/*[ENDIF] JAVA_SPEC_VERSION < 11956* @see ThreadDeath957*/958@Override959public void uncaughtException(Thread t, Throwable e) {960Thread.UncaughtExceptionHandler handler;961/*[PR 95801]*/962if (parent != null) {963parent.uncaughtException(t,e);964} else if ((handler = Thread.getDefaultUncaughtExceptionHandler()) != null) {965handler.uncaughtException(t, e);966} else if (!(e instanceof ThreadDeath)) {967// No parent group, has to be 'system' Thread Group968/*[MSG "K0319", "Exception in thread \"{0}\" "]*/969System.err.print(com.ibm.oti.util.Msg.getString("K0319", t.getName())); //$NON-NLS-1$970e.printStackTrace(System.err);971}972}973}974975976