| /* |
| * 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 java.lang; |
| |
| /** |
| * A {@code ThreadGroup} is a means of organizing {@link Thread}s into a |
| * hierarchical structure. A {@code ThreadGroup} can contain zero or more |
| * {@code Thread}s and zero or more other {@code ThreadGroup}s. Each {@code |
| * Thread} and each {@code ThreadGroup} (except the root group) has a unique |
| * parent {@code ThreadGroup}. The result is a tree whose inner nodes are |
| * {@code ThreadGroup}s and whose leaf nodes are {@code Threads}. The unique |
| * root of the tree is a {@code ThreadGroup} that is created at VM startup and |
| * has the name "system". The benefit of using {@code ThreadGroup}s, in addition |
| * to the mere housekeeping aspect, is that all {@code Thread}s in a {@code |
| * ThreadGroup} can be manipulated together, that is, the {@code ThreadGroup} |
| * has methods that delegate to all its all {@code Thread}s. |
| * |
| * @see Thread |
| * @see SecurityManager |
| */ |
| public class ThreadGroup implements Thread.UncaughtExceptionHandler { |
| |
| /* |
| * An implementation of this class is provided, but the documented |
| * constructors are used by the vm specific implementation to create the |
| * required "system" and "main" ThreadGroups. The documented methods are |
| * used by java.lang.Thread to add and remove Threads from their |
| * ThreadGroups. |
| */ |
| |
| // Name of this ThreadGroup |
| private String name; |
| |
| // Maximum priority for Threads inside this ThreadGroup |
| private int maxPriority = Thread.MAX_PRIORITY; |
| |
| // The ThreadGroup to which this ThreadGroup belongs |
| ThreadGroup parent; |
| |
| int numThreads; |
| |
| // The Threads this ThreadGroup contains |
| private Thread[] childrenThreads = new Thread[5]; |
| |
| // The number of children groups |
| int numGroups; |
| |
| // The ThreadGroups this ThreadGroup contains |
| private ThreadGroup[] childrenGroups = new ThreadGroup[3]; |
| |
| // Locked when using the childrenGroups field |
| private class ChildrenGroupsLock {} |
| private Object childrenGroupsLock = new ChildrenGroupsLock(); |
| |
| // Locked when using the childrenThreads field |
| private class ChildrenThreadsLock {} |
| private Object childrenThreadsLock = new ChildrenThreadsLock(); |
| |
| // Whether this ThreadGroup is a daemon ThreadGroup or not |
| private boolean isDaemon; |
| |
| // Whether this ThreadGroup has already been destroyed or not |
| private boolean isDestroyed; |
| |
| /** |
| * Used by the JVM to create the "system" ThreadGroup. Construct a |
| * ThreadGroup instance, and assign the name "system". |
| */ |
| private ThreadGroup() { |
| name = "system"; |
| } |
| |
| /** |
| * Constructs a new ThreadGroup with the name provided. The new ThreadGroup |
| * will be child of the ThreadGroup to which the |
| * <code>Thread.currentThread()</code> belongs. |
| * |
| * @param name Name for the ThreadGroup being created |
| * |
| * @throws SecurityException if <code>checkAccess()</code> for the parent |
| * group fails with a SecurityException |
| * |
| * @see java.lang.Thread#currentThread |
| */ |
| |
| public ThreadGroup(String name) { |
| this(Thread.currentThread().getThreadGroup(), name); |
| } |
| |
| /** |
| * Constructs a new ThreadGroup with the name provided, as child of the |
| * ThreadGroup <code>parent</code> |
| * |
| * @param parent Parent ThreadGroup |
| * @param name Name for the ThreadGroup being created |
| * |
| * @throws NullPointerException if <code>parent</code> is |
| * <code>null</code> |
| * @throws SecurityException if <code>checkAccess()</code> for the parent |
| * group fails with a SecurityException |
| * @throws IllegalThreadStateException if <code>parent</code> has been |
| * destroyed already |
| */ |
| public ThreadGroup(ThreadGroup parent, String name) { |
| super(); |
| if (Thread.currentThread() != null) { |
| // If parent is null we must throw NullPointerException, but that |
| // will be done "for free" with the message send below |
| parent.checkAccess(); |
| } |
| |
| this.name = name; |
| this.setParent(parent); |
| if (parent != null) { |
| this.setMaxPriority(parent.getMaxPriority()); |
| if (parent.isDaemon()) { |
| this.setDaemon(true); |
| } |
| } |
| } |
| |
| /** |
| * Initialize the "main" ThreadGroup |
| */ |
| ThreadGroup(ThreadGroup parent) { |
| this.name = "main"; |
| this.setParent(parent); |
| } |
| |
| /** |
| * Returns the number of Threads which are children of the receiver, |
| * directly or indirectly and are running. |
| * |
| * @return the number of children Threads |
| */ |
| public int activeCount() { |
| int count = numThreads; |
| // Lock this subpart of the tree as we walk |
| synchronized (this.childrenGroupsLock) { |
| for (int i = 0; i < numGroups; i++) { |
| count += this.childrenGroups[i].activeCount(); |
| } |
| } |
| return count; |
| } |
| |
| /** |
| * Returns the number of ThreadGroups which are children of the receiver, |
| * directly or indirectly. |
| * |
| * @return the number of children ThreadGroups |
| */ |
| public int activeGroupCount() { |
| int count = 0; |
| // Lock this subpart of the tree as we walk |
| synchronized (this.childrenGroupsLock) { |
| for (int i = 0; i < numGroups; i++) { |
| // One for this group & the subgroups |
| count += 1 + this.childrenGroups[i].activeGroupCount(); |
| } |
| } |
| return count; |
| } |
| |
| /** |
| * Adds a Thread to the receiver. This should only be visible to class |
| * java.lang.Thread, and should only be called when a new Thread is created |
| * and initialized by the constructor. |
| * |
| * @param thread Thread to add to the receiver |
| * |
| * @throws IllegalThreadStateException if the receiver has been destroyed |
| * already |
| * |
| * @see #remove(java.lang.Thread) |
| */ |
| final void add(Thread thread) throws IllegalThreadStateException { |
| synchronized (this.childrenThreadsLock) { |
| if (!isDestroyed) { |
| if (childrenThreads.length == numThreads) { |
| Thread[] newThreads = new Thread[childrenThreads.length * 2]; |
| System.arraycopy(childrenThreads, 0, newThreads, 0, numThreads); |
| newThreads[numThreads++] = thread; |
| childrenThreads = newThreads; |
| } else { |
| childrenThreads[numThreads++] = thread; |
| } |
| } else { |
| throw new IllegalThreadStateException(); |
| } |
| } |
| } |
| |
| /** |
| * Adds a ThreadGroup to the receiver. |
| * |
| * @param g ThreadGroup to add to the receiver |
| * |
| * @throws IllegalThreadStateException if the receiver has been destroyed |
| * already |
| */ |
| private void add(ThreadGroup g) throws IllegalThreadStateException { |
| synchronized (this.childrenGroupsLock) { |
| if (!isDestroyed) { |
| if (childrenGroups.length == numGroups) { |
| ThreadGroup[] newGroups = new ThreadGroup[childrenGroups.length * 2]; |
| System.arraycopy(childrenGroups, 0, newGroups, 0, numGroups); |
| newGroups[numGroups++] = g; |
| childrenGroups = newGroups; |
| } else { |
| childrenGroups[numGroups++] = g; |
| } |
| } else { |
| throw new IllegalThreadStateException(); |
| } |
| } |
| } |
| |
| /** |
| * Does nothing. The definition of this method depends on the deprecated |
| * method {@link #suspend()}. The exact behavior of this call was never |
| * specified. |
| * |
| * @param b Used to control low memory implicit suspension |
| * @return {@code true} (always) |
| * |
| * @deprecated Required deprecated method suspend(). |
| */ |
| @Deprecated |
| public boolean allowThreadSuspension(boolean b) { |
| // Does not apply to this VM, no-op |
| return true; |
| } |
| |
| /** |
| * Checks the accessibility of the ThreadGroup from the perspective of the |
| * caller. If there is a SecurityManager installed, calls |
| * {@code checkAccess} with the receiver as a parameter, otherwise does |
| * nothing. |
| */ |
| public final void checkAccess() { |
| // Forwards the message to the SecurityManager (if there's one) passing |
| // the receiver as parameter |
| SecurityManager currentManager = System.getSecurityManager(); |
| if (currentManager != null) { |
| currentManager.checkAccess(this); |
| } |
| } |
| |
| /** |
| * Destroys the receiver and recursively all its subgroups. It is only legal |
| * to destroy a ThreadGroup that has no Threads in it. Any daemon |
| * ThreadGroup is destroyed automatically when it becomes empty (no Threads |
| * and no ThreadGroups in it). |
| * |
| * @throws IllegalThreadStateException if the receiver or any of its |
| * subgroups has been destroyed already or if it still contains |
| * threads. |
| * @throws SecurityException if {@code this.checkAccess()} fails with |
| * a SecurityException |
| */ |
| |
| public final void destroy() { |
| checkAccess(); |
| |
| // Lock this subpart of the tree as we walk |
| synchronized (this.childrenThreadsLock) { |
| synchronized (this.childrenGroupsLock) { |
| int toDestroy = numGroups; |
| // Call recursively for subgroups |
| for (int i = 0; i < toDestroy; i++) { |
| // We always get the first element - remember, when the |
| // child dies it removes itself from our collection. See |
| // below. |
| this.childrenGroups[0].destroy(); |
| } |
| |
| if (parent != null) { |
| parent.remove(this); |
| } |
| |
| // Now that the ThreadGroup is really destroyed it can be tagged |
| // as so |
| this.isDestroyed = true; |
| } |
| } |
| } |
| |
| /* |
| * Auxiliary method that destroys the receiver and recursively all its |
| * subgroups if the receiver is a daemon ThreadGroup. |
| * |
| * @see #destroy |
| * @see #setDaemon |
| * @see #isDaemon |
| */ |
| private void destroyIfEmptyDaemon() { |
| // Has to be non-destroyed daemon to make sense |
| synchronized (this.childrenThreadsLock) { |
| if (isDaemon && !isDestroyed && numThreads == 0) { |
| synchronized (this.childrenGroupsLock) { |
| if (numGroups == 0) { |
| destroy(); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Iterates over all active threads in this group (and its sub-groups) and |
| * stores the threads in the given array. Returns when the array is full or |
| * no more threads remain, whichever happens first. |
| * |
| * @param threads the array into which the Threads will be copied |
| * @return the number of Threads that were copied |
| */ |
| public int enumerate(Thread[] threads) { |
| return enumerate(threads, true); |
| } |
| |
| /** |
| * Iterates over all active threads in this group (and, optionally, its |
| * sub-groups) and stores the threads in the given array. Returns when the |
| * array is full or no more threads remain, whichever happens first. |
| * |
| * @param threads the array into which the Threads will be copied |
| * @param recurse indicates whether Threads in subgroups should be |
| * recursively copied as well |
| * @return the number of Threads that were copied |
| */ |
| public int enumerate(Thread[] threads, boolean recurse) { |
| return enumerateGeneric(threads, recurse, 0, true); |
| } |
| |
| /** |
| * Iterates over all thread groups in this group (and its sub-groups) and |
| * and stores the groups in the given array. Returns when the array is full |
| * or no more groups remain, whichever happens first. |
| * |
| * @param groups the array into which the ThreadGroups will be copied |
| * @return the number of ThreadGroups that were copied |
| * |
| */ |
| public int enumerate(ThreadGroup[] groups) { |
| return enumerate(groups, true); |
| } |
| |
| /** |
| * Iterates over all thread groups in this group (and, optionally, its |
| * sub-groups) and and stores the groups in the given array. Returns when |
| * the array is full or no more groups remain, whichever happens first. |
| * |
| * @param groups the array into which the ThreadGroups will be copied |
| * @param recurse indicates whether ThreadGroups in subgroups should be |
| * recursively copied as well or not |
| * @return the number of ThreadGroups that were copied |
| * |
| */ |
| public int enumerate(ThreadGroup[] groups, boolean recurse) { |
| return enumerateGeneric(groups, recurse, 0, false); |
| } |
| |
| /** |
| * Copies into <param>enumeration</param> starting at |
| * <param>enumerationIndex</param> all Threads or ThreadGroups in the |
| * receiver. If <param>recurse</param> is true, recursively enumerate the |
| * elements in subgroups. |
| * |
| * If the array passed as parameter is too small no exception is thrown - |
| * the extra elements are simply not copied. |
| * |
| * @param enumeration array into which the elements will be copied |
| * @param recurse Indicates whether subgroups should be enumerated or not |
| * @param enumerationIndex Indicates in which position of the enumeration |
| * array we are |
| * @param enumeratingThreads Indicates whether we are enumerating Threads or |
| * ThreadGroups |
| * @return How many elements were enumerated/copied over |
| */ |
| private int enumerateGeneric(Object[] enumeration, boolean recurse, int enumerationIndex, |
| boolean enumeratingThreads) { |
| checkAccess(); |
| |
| Object[] immediateCollection = enumeratingThreads ? (Object[]) childrenThreads |
| : (Object[]) childrenGroups; |
| Object syncLock = enumeratingThreads ? childrenThreadsLock : childrenGroupsLock; |
| |
| synchronized (syncLock) { // Lock this subpart of the tree as we walk |
| for (int i = enumeratingThreads ? numThreads : numGroups; --i >= 0;) { |
| if (!enumeratingThreads || ((Thread) immediateCollection[i]).isAlive()) { |
| if (enumerationIndex >= enumeration.length) { |
| return enumerationIndex; |
| } |
| enumeration[enumerationIndex++] = immediateCollection[i]; |
| } |
| } |
| } |
| |
| if (recurse) { // Lock this subpart of the tree as we walk |
| synchronized (this.childrenGroupsLock) { |
| for (int i = 0; i < numGroups; i++) { |
| if (enumerationIndex >= enumeration.length) { |
| return enumerationIndex; |
| } |
| enumerationIndex = childrenGroups[i].enumerateGeneric(enumeration, recurse, |
| enumerationIndex, enumeratingThreads); |
| } |
| } |
| } |
| return enumerationIndex; |
| } |
| |
| /** |
| * Returns the maximum allowed priority for a Thread in the receiver. |
| * |
| * @return the maximum priority |
| * |
| * @see #setMaxPriority |
| */ |
| public final int getMaxPriority() { |
| return maxPriority; |
| } |
| |
| /** |
| * Returns the name of the receiver. |
| * |
| * @return the receiver's name |
| */ |
| public final String getName() { |
| return name; |
| } |
| |
| /** |
| * Returns the receiver's parent ThreadGroup. It can be {@code null} if the |
| * receiver is the the root ThreadGroup. |
| * |
| * @return the parent ThreadGroup |
| * |
| */ |
| public final ThreadGroup getParent() { |
| if (parent != null) { |
| parent.checkAccess(); |
| } |
| return parent; |
| } |
| |
| /** |
| * Interrupts every Thread in the receiver and recursively in all its |
| * subgroups. |
| * |
| * @throws SecurityException if {@code this.checkAccess()} fails with |
| * a SecurityException |
| * |
| * @see Thread#interrupt |
| */ |
| public final void interrupt() { |
| checkAccess(); |
| // Lock this subpart of the tree as we walk |
| synchronized (this.childrenThreadsLock) { |
| for (int i = 0; i < numThreads; i++) { |
| this.childrenThreads[i].interrupt(); |
| } |
| } |
| // Lock this subpart of the tree as we walk |
| synchronized (this.childrenGroupsLock) { |
| for (int i = 0; i < numGroups; i++) { |
| this.childrenGroups[i].interrupt(); |
| } |
| } |
| } |
| |
| /** |
| * Checks whether the receiver is a daemon ThreadGroup. |
| * |
| * @return true if (and only if) the receiver is a daemon ThreadGroup |
| * |
| * @see #setDaemon |
| * @see #destroy |
| */ |
| public final boolean isDaemon() { |
| return isDaemon; |
| } |
| |
| /** |
| * Checks whether the receiver has already been destroyed. |
| * |
| * @return true if (and only if) the receiver has already been destroyed |
| * |
| * @see #destroy |
| */ |
| public boolean isDestroyed() { |
| return isDestroyed; |
| } |
| |
| /** |
| * Outputs to {@code System.out} a text representation of the |
| * hierarchy of Threads and ThreadGroups in the receiver (and recursively). |
| * Proper indentation is done to suggest the nesting of groups inside groups |
| * and threads inside groups. |
| */ |
| public void list() { |
| // We start in a fresh line |
| System.out.println(); |
| list(0); |
| } |
| |
| /* |
| * Outputs to {@code System.out}a text representation of the |
| * hierarchy of Threads and ThreadGroups in the receiver (and recursively). |
| * The indentation will be four spaces per level of nesting. |
| * |
| * @param levels How many levels of nesting, so that proper indentation can |
| * be output. |
| */ |
| private void list(int levels) { |
| for (int i = 0; i < levels; i++) { |
| System.out.print(" "); // 4 spaces for each level |
| } |
| |
| // Print the receiver |
| System.out.println(this.toString()); |
| |
| // Print the children threads, with 1 extra indentation |
| synchronized (this.childrenThreadsLock) { |
| for (int i = 0; i < numThreads; i++) { |
| // children get an extra indentation, 4 spaces for each level |
| for (int j = 0; j <= levels; j++) { |
| System.out.print(" "); |
| } |
| System.out.println(this.childrenThreads[i]); |
| } |
| } |
| synchronized (this.childrenGroupsLock) { |
| for (int i = 0; i < numGroups; i++) { |
| this.childrenGroups[i].list(levels + 1); |
| } |
| } |
| } |
| |
| /** |
| * Checks whether the receiver is a direct or indirect parent group of a |
| * given ThreadGroup. |
| * |
| * @param g the potential child ThreadGroup |
| * |
| * @return true if (and only if) the receiver is parent of {@code g} |
| * |
| */ |
| public final boolean parentOf(ThreadGroup g) { |
| while (g != null) { |
| if (this == g) { |
| return true; |
| } |
| g = g.parent; |
| } |
| return false; |
| } |
| |
| /** |
| * Removes a Thread from the receiver. This should only be visible to class |
| * java.lang.Thread, and should only be called when a Thread dies. |
| * |
| * @param thread Thread to remove from the receiver |
| * |
| * @see #add(Thread) |
| */ |
| final void remove(java.lang.Thread thread) { |
| synchronized (this.childrenThreadsLock) { |
| for (int i = 0; i < numThreads; i++) { |
| if (childrenThreads[i].equals(thread)) { |
| numThreads--; |
| System |
| .arraycopy(childrenThreads, i + 1, childrenThreads, i, numThreads |
| - i); |
| childrenThreads[numThreads] = null; |
| break; |
| } |
| } |
| } |
| destroyIfEmptyDaemon(); |
| } |
| |
| /** |
| * Removes an immediate subgroup from the receiver. |
| * |
| * @param g ThreadGroup to remove from the receiver |
| * |
| * @see #add(Thread) |
| * @see #add(ThreadGroup) |
| */ |
| private void remove(ThreadGroup g) { |
| synchronized (this.childrenGroupsLock) { |
| for (int i = 0; i < numGroups; i++) { |
| if (childrenGroups[i].equals(g)) { |
| numGroups--; |
| System.arraycopy(childrenGroups, i + 1, childrenGroups, i, numGroups - i); |
| childrenGroups[numGroups] = null; |
| break; |
| } |
| } |
| } |
| destroyIfEmptyDaemon(); |
| } |
| |
| /** |
| * Resumes every Thread in the receiver and recursively in all its |
| * subgroups. |
| * |
| * @throws SecurityException if {@code this.checkAccess()} fails with |
| * a SecurityException |
| * |
| * @see Thread#resume |
| * @see #suspend |
| * |
| * @deprecated Requires deprecated method Thread.resume(). |
| */ |
| @SuppressWarnings("deprecation") |
| @Deprecated |
| public final void resume() { |
| checkAccess(); |
| // Lock this subpart of the tree as we walk |
| synchronized (this.childrenThreadsLock) { |
| for (int i = 0; i < numThreads; i++) { |
| this.childrenThreads[i].resume(); |
| } |
| } |
| // Lock this subpart of the tree as we walk |
| synchronized (this.childrenGroupsLock) { |
| for (int i = 0; i < numGroups; i++) { |
| this.childrenGroups[i].resume(); |
| } |
| } |
| } |
| |
| /** |
| * Configures the receiver to be a daemon ThreadGroup or not. Daemon |
| * ThreadGroups are automatically destroyed when they become empty. |
| * |
| * @param isDaemon the new value defining if receiver should be daemon or |
| * not |
| * |
| * @throws SecurityException if {@code checkAccess()} for the parent |
| * group fails with a SecurityException |
| * |
| * @see #isDaemon |
| * @see #destroy |
| */ |
| public final void setDaemon(boolean isDaemon) { |
| checkAccess(); |
| this.isDaemon = isDaemon; |
| } |
| |
| /** |
| * Configures the maximum allowed priority for a Thread in the receiver and |
| * recursively in all its subgroups. |
| * |
| * One can never change the maximum priority of a ThreadGroup to be higher |
| * than it was. Such an attempt will not result in an exception, it will |
| * simply leave the ThreadGroup with its current maximum priority. |
| * |
| * @param newMax the new maximum priority to be set |
| * |
| * @throws SecurityException if {@code checkAccess()} fails with a |
| * SecurityException |
| * @throws IllegalArgumentException if the new priority is greater than |
| * Thread.MAX_PRIORITY or less than Thread.MIN_PRIORITY |
| * |
| * @see #getMaxPriority |
| */ |
| public final void setMaxPriority(int newMax) { |
| checkAccess(); |
| |
| if (newMax <= this.maxPriority) { |
| if (newMax < Thread.MIN_PRIORITY) { |
| newMax = Thread.MIN_PRIORITY; |
| } |
| |
| int parentPriority = parent == null ? newMax : parent.getMaxPriority(); |
| this.maxPriority = parentPriority <= newMax ? parentPriority : newMax; |
| // Lock this subpart of the tree as we walk |
| synchronized (this.childrenGroupsLock) { |
| // ??? why not maxPriority |
| for (int i = 0; i < numGroups; i++) { |
| this.childrenGroups[i].setMaxPriority(newMax); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Sets the parent ThreadGroup of the receiver, and adds the receiver to the |
| * parent's collection of immediate children (if {@code parent} is |
| * not {@code null}). |
| * |
| * @param parent The parent ThreadGroup, or null if the receiver is to be |
| * the root ThreadGroup |
| * |
| * @see #getParent |
| * @see #parentOf |
| */ |
| private void setParent(ThreadGroup parent) { |
| if (parent != null) { |
| parent.add(this); |
| } |
| this.parent = parent; |
| } |
| |
| /** |
| * Stops every Thread in the receiver and recursively in all its subgroups. |
| * |
| * @throws SecurityException if {@code this.checkAccess()} fails with |
| * a SecurityException |
| * |
| * @see Thread#stop() |
| * @see Thread#stop(Throwable) |
| * @see ThreadDeath |
| * |
| * @deprecated Requires deprecated method Thread.stop(). |
| */ |
| @SuppressWarnings("deprecation") |
| @Deprecated |
| public final void stop() { |
| if (stopHelper()) { |
| Thread.currentThread().stop(); |
| } |
| } |
| |
| /** |
| * @deprecated Requires deprecated method Thread.suspend(). |
| */ |
| @SuppressWarnings("deprecation") |
| @Deprecated |
| private final boolean stopHelper() { |
| checkAccess(); |
| |
| boolean stopCurrent = false; |
| // Lock this subpart of the tree as we walk |
| synchronized (this.childrenThreadsLock) { |
| Thread current = Thread.currentThread(); |
| for (int i = 0; i < numThreads; i++) { |
| if (this.childrenThreads[i] == current) { |
| stopCurrent = true; |
| } else { |
| this.childrenThreads[i].stop(); |
| } |
| } |
| } |
| // Lock this subpart of the tree as we walk |
| synchronized (this.childrenGroupsLock) { |
| for (int i = 0; i < numGroups; i++) { |
| stopCurrent |= this.childrenGroups[i].stopHelper(); |
| } |
| } |
| return stopCurrent; |
| } |
| |
| /** |
| * Suspends every Thread in the receiver and recursively in all its |
| * subgroups. |
| * |
| * @throws SecurityException if {@code this.checkAccess()} fails with |
| * a SecurityException |
| * |
| * @see Thread#suspend |
| * @see #resume |
| * |
| * @deprecated Requires deprecated method Thread.suspend(). |
| */ |
| @SuppressWarnings("deprecation") |
| @Deprecated |
| public final void suspend() { |
| if (suspendHelper()) { |
| Thread.currentThread().suspend(); |
| } |
| } |
| |
| /** |
| * @deprecated Requires deprecated method Thread.suspend(). |
| */ |
| @SuppressWarnings("deprecation") |
| @Deprecated |
| private final boolean suspendHelper() { |
| checkAccess(); |
| |
| boolean suspendCurrent = false; |
| // Lock this subpart of the tree as we walk |
| synchronized (this.childrenThreadsLock) { |
| Thread current = Thread.currentThread(); |
| for (int i = 0; i < numThreads; i++) { |
| if (this.childrenThreads[i] == current) { |
| suspendCurrent = true; |
| } else { |
| this.childrenThreads[i].suspend(); |
| } |
| } |
| } |
| // Lock this subpart of the tree as we walk |
| synchronized (this.childrenGroupsLock) { |
| for (int i = 0; i < numGroups; i++) { |
| suspendCurrent |= this.childrenGroups[i].suspendHelper(); |
| } |
| } |
| return suspendCurrent; |
| } |
| |
| /** |
| * Returns a string containing a concise, human-readable description of the |
| * receiver. |
| * |
| * @return a printable representation of the ThreadGroup |
| */ |
| @Override |
| public String toString() { |
| return getClass().getName() + "[name=" + this.getName() + ",maxpri=" |
| + this.getMaxPriority() + "]"; |
| } |
| |
| /** |
| * Handles uncaught exceptions. Any uncaught exception in any Thread |
| * is forwarded (by the VM) to the Thread's ThreadGroup by sending this |
| * message (uncaughtException). This allows users to define custom |
| * ThreadGroup classes and custom behavior for when a Thread has an |
| * uncaughtException or when it does (ThreadDeath). |
| * |
| * @param t the Thread that terminated with an uncaught exception |
| * @param e the uncaught exception itself |
| * |
| * @see Thread#stop() |
| * @see Thread#stop(Throwable) |
| * @see ThreadDeath |
| */ |
| public void uncaughtException(Thread t, Throwable e) { |
| if (parent != null) { |
| parent.uncaughtException(t, e); |
| } else if (!(e instanceof ThreadDeath)) { |
| // No parent group, has to be 'system' Thread Group |
| e.printStackTrace(System.err); |
| } |
| } |
| } |