• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package java.lang;
19 
20 /**
21  * {@code ThreadGroup} is a means of organizing threads into a hierarchical structure.
22  * This class is obsolete. See <i>Effective Java</i> Item 73, "Avoid thread groups" for details.
23  * @see Thread
24  * @see SecurityManager
25  */
26 public class ThreadGroup implements Thread.UncaughtExceptionHandler {
27 
28     // Name of this ThreadGroup
29     private String name;
30     // BEGIN android-note
31     // VM needs this field name for debugging.
32     // END android-note
33 
34     // Maximum priority for Threads inside this ThreadGroup
35     private int maxPriority = Thread.MAX_PRIORITY;
36 
37     // The ThreadGroup to which this ThreadGroup belongs
38     ThreadGroup parent;
39     // BEGIN android-note
40     // VM needs this field name for debugging.
41     // END android-note
42 
43     int numThreads;
44 
45     // The Threads this ThreadGroup contains
46     private Thread[] childrenThreads = new Thread[5];
47 
48     // The number of children groups
49     int numGroups;
50 
51     // The ThreadGroups this ThreadGroup contains
52     private ThreadGroup[] childrenGroups = new ThreadGroup[3];
53 
54     // Locked when using the childrenGroups field
55     private class ChildrenGroupsLock {}
56     private Object childrenGroupsLock = new ChildrenGroupsLock();
57 
58     // Locked when using the childrenThreads field
59     private class ChildrenThreadsLock {}
60     private Object childrenThreadsLock = new ChildrenThreadsLock();
61 
62     // Whether this ThreadGroup is a daemon ThreadGroup or not
63     private boolean isDaemon;
64 
65     // Whether this ThreadGroup has already been destroyed or not
66     private boolean isDestroyed;
67 
68     // BEGIN android-added
69     /* the VM uses these directly; do not rename */
70     static ThreadGroup mSystem = new ThreadGroup();
71     static ThreadGroup mMain = new ThreadGroup(mSystem, "main");
72     // END android-added
73 
74     // BEGIN android-removed
75     // /**
76     //  * Used by the JVM to create the "system" ThreadGroup. Construct a
77     //  * ThreadGroup instance, and assign the name "system".
78     //  */
79     // private ThreadGroup() {
80     //     name = "system";
81     // }
82     // END android-removed
83 
84     /**
85      * Constructs a new {@code ThreadGroup} with the given name. The new {@code ThreadGroup}
86      * will be child of the {@code ThreadGroup} to which the calling thread belongs.
87      *
88      * @param name the name
89      * @throws SecurityException if {@code checkAccess()} for the parent
90      *         group fails with a SecurityException
91      * @see java.lang.Thread#currentThread
92      */
93 
ThreadGroup(String name)94     public ThreadGroup(String name) {
95         this(Thread.currentThread().getThreadGroup(), name);
96     }
97 
98     /**
99      * Constructs a new {@code ThreadGroup} with the given name, as a child of the
100      * given {@code ThreadGroup}.
101      *
102      * @param parent the parent
103      * @param name the name
104      * @throws NullPointerException if {@code parent == null}
105      * @throws SecurityException if {@code checkAccess()} for the parent
106      *         group fails with a SecurityException
107      * @throws IllegalThreadStateException if {@code parent} has been
108      *         destroyed already
109      */
ThreadGroup(ThreadGroup parent, String name)110     public ThreadGroup(ThreadGroup parent, String name) {
111         super();
112         if (Thread.currentThread() != null) {
113             // If parent is null we must throw NullPointerException, but that
114             // will be done "for free" with the message send below
115             parent.checkAccess();
116         }
117 
118         this.name = name;
119         this.setParent(parent);
120         if (parent != null) {
121             this.setMaxPriority(parent.getMaxPriority());
122             if (parent.isDaemon()) {
123                 this.setDaemon(true);
124             }
125         }
126     }
127 
128     /**
129      * Initialize the special "system" ThreadGroup. Was "main" in Harmony,
130      * but we have an additional group above that in Android.
131      */
ThreadGroup()132     ThreadGroup() {
133         this.name = "system";
134         this.setParent(null);
135     }
136 
137     /**
138      * Returns the number of running {@code Thread}s which are children of this thread group,
139      * directly or indirectly.
140      *
141      * @return the number of children
142      */
activeCount()143     public int activeCount() {
144         // BEGIN android-changed
145         int count = 0;
146         // Lock the children thread list
147         synchronized (this.childrenThreadsLock) {
148             for (int i = 0; i < numThreads; i++) {
149                 if(childrenThreads[i].isAlive()) {
150                     count++;
151                 }
152             }
153         }
154         // END android-changed
155         // Lock this subpart of the tree as we walk
156         synchronized (this.childrenGroupsLock) {
157             for (int i = 0; i < numGroups; i++) {
158                 count += this.childrenGroups[i].activeCount();
159             }
160         }
161         return count;
162     }
163 
164     /**
165      * Returns the number of {@code ThreadGroup}s which are children of this group,
166      * directly or indirectly.
167      *
168      * @return the number of children
169      */
activeGroupCount()170     public int activeGroupCount() {
171         int count = 0;
172         // Lock this subpart of the tree as we walk
173         synchronized (this.childrenGroupsLock) {
174             for (int i = 0; i < numGroups; i++) {
175                 // One for this group & the subgroups
176                 count += 1 + this.childrenGroups[i].activeGroupCount();
177             }
178         }
179         return count;
180     }
181 
182     /**
183      * Adds a {@code Thread} to this thread group. This should only be visible to class
184      * java.lang.Thread, and should only be called when a new Thread is created
185      * and initialized by the constructor.
186      *
187      * @param thread Thread to add
188      * @throws IllegalThreadStateException if this group has been destroyed already
189      * @see #remove(java.lang.Thread)
190      */
add(Thread thread)191     final void add(Thread thread) throws IllegalThreadStateException {
192         synchronized (this.childrenThreadsLock) {
193             if (!isDestroyed) {
194                 if (childrenThreads.length == numThreads) {
195                     Thread[] newThreads = new Thread[childrenThreads.length * 2];
196                     System.arraycopy(childrenThreads, 0, newThreads, 0, numThreads);
197                     newThreads[numThreads++] = thread;
198                     childrenThreads = newThreads;
199                 } else {
200                     childrenThreads[numThreads++] = thread;
201                 }
202             } else {
203                 throw new IllegalThreadStateException();
204             }
205         }
206     }
207 
208     /**
209      * Adds a {@code ThreadGroup} to this thread group.
210      *
211      * @param g ThreadGroup to add
212      * @throws IllegalThreadStateException if this group has been destroyed already
213      */
add(ThreadGroup g)214     private void add(ThreadGroup g) throws IllegalThreadStateException {
215         synchronized (this.childrenGroupsLock) {
216             if (!isDestroyed) {
217                 if (childrenGroups.length == numGroups) {
218                     ThreadGroup[] newGroups = new ThreadGroup[childrenGroups.length * 2];
219                     System.arraycopy(childrenGroups, 0, newGroups, 0, numGroups);
220                     newGroups[numGroups++] = g;
221                     childrenGroups = newGroups;
222                 } else {
223                     childrenGroups[numGroups++] = g;
224                 }
225             } else {
226                 throw new IllegalThreadStateException();
227             }
228         }
229     }
230 
231     /**
232      * Does nothing. The definition of this method depends on the deprecated
233      * method {@link #suspend()}. The exact behavior of this call was never
234      * specified.
235      *
236      * @param b Used to control low memory implicit suspension
237      * @return {@code true} (always)
238      *
239      * @deprecated Required deprecated method suspend().
240      */
241     @Deprecated
allowThreadSuspension(boolean b)242     public boolean allowThreadSuspension(boolean b) {
243         // Does not apply to this VM, no-op
244         return true;
245     }
246 
247     /**
248      * Checks the accessibility of this {@code ThreadGroup} from the perspective of the
249      * caller. If there is a {@code SecurityManager} installed, calls
250      * {@code checkAccess} with this thread group as a parameter, otherwise does
251      * nothing.
252      */
checkAccess()253     public final void checkAccess() {
254         SecurityManager currentManager = System.getSecurityManager();
255         if (currentManager != null) {
256             currentManager.checkAccess(this);
257         }
258     }
259 
260     /**
261      * Destroys this thread group and recursively all its subgroups. It is only legal
262      * to destroy a {@code ThreadGroup} that has no threads in it. Any daemon
263      * {@code ThreadGroup} is destroyed automatically when it becomes empty (no threads
264      * or thread groups in it).
265      *
266      * @throws IllegalThreadStateException if this thread group or any of its
267      *         subgroups has been destroyed already or if it still contains
268      *         threads.
269      * @throws SecurityException if {@code this.checkAccess()} fails with
270      *         a SecurityException
271      */
destroy()272     public final void destroy() {
273         checkAccess();
274 
275         // Lock this subpart of the tree as we walk
276         synchronized (this.childrenThreadsLock) {
277             synchronized (this.childrenGroupsLock) {
278                 // BEGIN android-added
279                 if (this.isDestroyed) {
280                     throw new IllegalThreadStateException(
281                             "Thread group was already destroyed: "
282                             + (this.name != null ? this.name : "n/a"));
283                 }
284                 if (this.numThreads > 0) {
285                     throw new IllegalThreadStateException(
286                             "Thread group still contains threads: "
287                             + (this.name != null ? this.name : "n/a"));
288                 }
289                 // END android-added
290                 int toDestroy = numGroups;
291                 // Call recursively for subgroups
292                 for (int i = 0; i < toDestroy; i++) {
293                     // We always get the first element - remember, when the
294                     // child dies it removes itself from our collection. See
295                     // below.
296                     this.childrenGroups[0].destroy();
297                 }
298 
299                 if (parent != null) {
300                     parent.remove(this);
301                 }
302 
303                 // Now that the ThreadGroup is really destroyed it can be tagged
304                 // as so
305                 this.isDestroyed = true;
306             }
307         }
308     }
309 
310     /*
311      * Auxiliary method that destroys this thread group and recursively all its
312      * subgroups if this is a daemon ThreadGroup.
313      *
314      * @see #destroy
315      * @see #setDaemon
316      * @see #isDaemon
317      */
destroyIfEmptyDaemon()318     private void destroyIfEmptyDaemon() {
319         // Has to be non-destroyed daemon to make sense
320         synchronized (this.childrenThreadsLock) {
321             if (isDaemon && !isDestroyed && numThreads == 0) {
322                 synchronized (this.childrenGroupsLock) {
323                     if (numGroups == 0) {
324                         destroy();
325                     }
326                 }
327             }
328         }
329     }
330 
331     /**
332      * Iterates over all active threads in this group (and its sub-groups) and
333      * stores the threads in the given array. Returns when the array is full or
334      * no more threads remain, whichever happens first.
335      *
336      * <p>Note that this method will silently ignore any threads that don't fit in the
337      * supplied array.
338      *
339      * @param threads the array into which the {@code Thread}s will be copied
340      * @return the number of {@code Thread}s that were copied
341      */
enumerate(Thread[] threads)342     public int enumerate(Thread[] threads) {
343         return enumerate(threads, true);
344     }
345 
346     /**
347      * Iterates over all active threads in this group (and, optionally, its
348      * sub-groups) and stores the threads in the given array. Returns when the
349      * array is full or no more threads remain, whichever happens first.
350      *
351      * <p>Note that this method will silently ignore any threads that don't fit in the
352      * supplied array.
353      *
354      * @param threads the array into which the {@code Thread}s will be copied
355      * @param recurse indicates whether {@code Thread}s in subgroups should be
356      *        recursively copied as well
357      * @return the number of {@code Thread}s that were copied
358      */
enumerate(Thread[] threads, boolean recurse)359     public int enumerate(Thread[] threads, boolean recurse) {
360         return enumerateGeneric(threads, recurse, 0, true);
361     }
362 
363     /**
364      * Iterates over all thread groups in this group (and its sub-groups) and
365      * and stores the groups in the given array. Returns when the array is full
366      * or no more groups remain, whichever happens first.
367      *
368      * <p>Note that this method will silently ignore any thread groups that don't fit in the
369      * supplied array.
370      *
371      * @param groups the array into which the {@code ThreadGroup}s will be copied
372      * @return the number of {@code ThreadGroup}s that were copied
373      */
enumerate(ThreadGroup[] groups)374     public int enumerate(ThreadGroup[] groups) {
375         return enumerate(groups, true);
376     }
377 
378     /**
379      * Iterates over all thread groups in this group (and, optionally, its
380      * sub-groups) and stores the groups in the given array. Returns when
381      * the array is full or no more groups remain, whichever happens first.
382      *
383      * <p>Note that this method will silently ignore any thread groups that don't fit in the
384      * supplied array.
385      *
386      * @param groups the array into which the {@code ThreadGroup}s will be copied
387      * @param recurse indicates whether {@code ThreadGroup}s in subgroups should be
388      *        recursively copied as well or not
389      * @return the number of {@code ThreadGroup}s that were copied
390      */
enumerate(ThreadGroup[] groups, boolean recurse)391     public int enumerate(ThreadGroup[] groups, boolean recurse) {
392         return enumerateGeneric(groups, recurse, 0, false);
393     }
394 
395     /**
396      * Copies into <param>enumeration</param> starting at
397      * <param>enumerationIndex</param> all Threads or ThreadGroups in the
398      * receiver. If <param>recurse</param> is true, recursively enumerate the
399      * elements in subgroups.
400      *
401      * If the array passed as parameter is too small no exception is thrown -
402      * the extra elements are simply not copied.
403      *
404      * @param enumeration array into which the elements will be copied
405      * @param recurse Indicates whether subgroups should be enumerated or not
406      * @param enumerationIndex Indicates in which position of the enumeration
407      *        array we are
408      * @param enumeratingThreads Indicates whether we are enumerating Threads or
409      *        ThreadGroups
410      * @return How many elements were enumerated/copied over
411      */
enumerateGeneric(Object[] enumeration, boolean recurse, int enumerationIndex, boolean enumeratingThreads)412     private int enumerateGeneric(Object[] enumeration, boolean recurse, int enumerationIndex,
413             boolean enumeratingThreads) {
414         checkAccess();
415 
416         Object[] immediateCollection = enumeratingThreads ? (Object[]) childrenThreads
417                 : (Object[]) childrenGroups;
418         Object syncLock = enumeratingThreads ? childrenThreadsLock : childrenGroupsLock;
419 
420         synchronized (syncLock) { // Lock this subpart of the tree as we walk
421             for (int i = enumeratingThreads ? numThreads : numGroups; --i >= 0;) {
422                 if (!enumeratingThreads || ((Thread) immediateCollection[i]).isAlive()) {
423                     if (enumerationIndex >= enumeration.length) {
424                         return enumerationIndex;
425                     }
426                     enumeration[enumerationIndex++] = immediateCollection[i];
427                 }
428             }
429         }
430 
431         if (recurse) { // Lock this subpart of the tree as we walk
432             synchronized (this.childrenGroupsLock) {
433                 for (int i = 0; i < numGroups; i++) {
434                     if (enumerationIndex >= enumeration.length) {
435                         return enumerationIndex;
436                     }
437                     enumerationIndex = childrenGroups[i].enumerateGeneric(enumeration, recurse,
438                             enumerationIndex, enumeratingThreads);
439                 }
440             }
441         }
442         return enumerationIndex;
443     }
444 
445     /**
446      * Returns the maximum allowed priority for a {@code Thread} in this thread group.
447      *
448      * @return the maximum priority
449      *
450      * @see #setMaxPriority
451      */
getMaxPriority()452     public final int getMaxPriority() {
453         return maxPriority;
454     }
455 
456     /**
457      * Returns the name of this thread group.
458      *
459      * @return the group's name
460      */
getName()461     public final String getName() {
462         return name;
463     }
464 
465     /**
466      * Returns this thread group's parent {@code ThreadGroup}. It can be null if this
467      * is the the root ThreadGroup.
468      *
469      * @return the parent
470      */
getParent()471     public final ThreadGroup getParent() {
472         if (parent != null) {
473             parent.checkAccess();
474         }
475         return parent;
476     }
477 
478     /**
479      * Interrupts every {@code Thread} in this group and recursively in all its
480      * subgroups.
481      *
482      * @throws SecurityException if {@code this.checkAccess()} fails with
483      *         a SecurityException
484      *
485      * @see Thread#interrupt
486      */
interrupt()487     public final void interrupt() {
488         checkAccess();
489         // Lock this subpart of the tree as we walk
490         synchronized (this.childrenThreadsLock) {
491             for (int i = 0; i < numThreads; i++) {
492                 this.childrenThreads[i].interrupt();
493             }
494         }
495         // Lock this subpart of the tree as we walk
496         synchronized (this.childrenGroupsLock) {
497             for (int i = 0; i < numGroups; i++) {
498                 this.childrenGroups[i].interrupt();
499             }
500         }
501     }
502 
503     /**
504      * Checks whether this thread group is a daemon {@code ThreadGroup}.
505      *
506      * @return true if this thread group is a daemon {@code ThreadGroup}
507      *
508      * @see #setDaemon
509      * @see #destroy
510      */
isDaemon()511     public final boolean isDaemon() {
512         return isDaemon;
513     }
514 
515     /**
516      * Checks whether this thread group has already been destroyed.
517      *
518      * @return true if this thread group has already been destroyed
519      * @see #destroy
520      */
isDestroyed()521     public synchronized boolean isDestroyed() {
522         return isDestroyed;
523     }
524 
525     /**
526      * Outputs to {@code System.out} a text representation of the
527      * hierarchy of {@code Thread}s and {@code ThreadGroup}s in this thread group (and recursively).
528      * Proper indentation is used to show the nesting of groups inside groups
529      * and threads inside groups.
530      */
list()531     public void list() {
532         // We start in a fresh line
533         System.out.println();
534         list(0);
535     }
536 
537     /*
538      * Outputs to {@code System.out}a text representation of the
539      * hierarchy of Threads and ThreadGroups in this thread group (and recursively).
540      * The indentation will be four spaces per level of nesting.
541      *
542      * @param levels How many levels of nesting, so that proper indentation can
543      * be output.
544      */
list(int levels)545     private void list(int levels) {
546         for (int i = 0; i < levels; i++) {
547             System.out.print("    "); // 4 spaces for each level
548         }
549 
550         // Print the receiver
551         System.out.println(this.toString());
552 
553         // Print the children threads, with 1 extra indentation
554         synchronized (this.childrenThreadsLock) {
555             for (int i = 0; i < numThreads; i++) {
556                 // children get an extra indentation, 4 spaces for each level
557                 for (int j = 0; j <= levels; j++) {
558                     System.out.print("    ");
559                 }
560                 System.out.println(this.childrenThreads[i]);
561             }
562         }
563         synchronized (this.childrenGroupsLock) {
564             for (int i = 0; i < numGroups; i++) {
565                 this.childrenGroups[i].list(levels + 1);
566             }
567         }
568     }
569 
570     /**
571      * Checks whether this thread group is a direct or indirect parent group of a
572      * given {@code ThreadGroup}.
573      *
574      * @param g the potential child {@code ThreadGroup}
575      * @return true if this thread group is parent of {@code g}
576      */
parentOf(ThreadGroup g)577     public final boolean parentOf(ThreadGroup g) {
578         while (g != null) {
579             if (this == g) {
580                 return true;
581             }
582             g = g.parent;
583         }
584         return false;
585     }
586 
587     /**
588      * Removes a {@code Thread} from this group. This should only be visible to class
589      * java.lang.Thread, and should only be called when a Thread dies.
590      *
591      * @param thread Thread to remove
592      *
593      * @see #add(Thread)
594      */
remove(java.lang.Thread thread)595     final void remove(java.lang.Thread thread) {
596         synchronized (this.childrenThreadsLock) {
597             for (int i = 0; i < numThreads; i++) {
598                 if (childrenThreads[i].equals(thread)) {
599                     numThreads--;
600                     System
601                             .arraycopy(childrenThreads, i + 1, childrenThreads, i, numThreads
602                                     - i);
603                     childrenThreads[numThreads] = null;
604                     break;
605                 }
606             }
607         }
608         destroyIfEmptyDaemon();
609     }
610 
611     /**
612      * Removes an immediate subgroup.
613      *
614      * @param g ThreadGroup to remove
615      *
616      * @see #add(Thread)
617      * @see #add(ThreadGroup)
618      */
remove(ThreadGroup g)619     private void remove(ThreadGroup g) {
620         synchronized (this.childrenGroupsLock) {
621             for (int i = 0; i < numGroups; i++) {
622                 if (childrenGroups[i].equals(g)) {
623                     numGroups--;
624                     System.arraycopy(childrenGroups, i + 1, childrenGroups, i, numGroups - i);
625                     childrenGroups[numGroups] = null;
626                     break;
627                 }
628             }
629         }
630         destroyIfEmptyDaemon();
631     }
632 
633     /**
634      * Resumes every thread in this group and recursively in all its
635      * subgroups.
636      *
637      * @throws SecurityException if {@code this.checkAccess()} fails with
638      *         a SecurityException
639      *
640      * @see Thread#resume
641      * @see #suspend
642      *
643      * @deprecated Requires deprecated method Thread.resume().
644      */
645     @SuppressWarnings("deprecation")
646     @Deprecated
resume()647     public final void resume() {
648         checkAccess();
649         // Lock this subpart of the tree as we walk
650         synchronized (this.childrenThreadsLock) {
651             for (int i = 0; i < numThreads; i++) {
652                 this.childrenThreads[i].resume();
653             }
654         }
655         // Lock this subpart of the tree as we walk
656         synchronized (this.childrenGroupsLock) {
657             for (int i = 0; i < numGroups; i++) {
658                 this.childrenGroups[i].resume();
659             }
660         }
661     }
662 
663     /**
664      * Sets whether this is a daemon {@code ThreadGroup} or not. Daemon
665      * thread groups are automatically destroyed when they become empty.
666      *
667      * @param isDaemon the new value
668      * @throws SecurityException if {@code checkAccess()} for the parent
669      *         group fails with a SecurityException
670      *
671      * @see #isDaemon
672      * @see #destroy
673      */
setDaemon(boolean isDaemon)674     public final void setDaemon(boolean isDaemon) {
675         checkAccess();
676         this.isDaemon = isDaemon;
677     }
678 
679     /**
680      * Configures the maximum allowed priority for a {@code Thread} in this group and
681      * recursively in all its subgroups.
682      *
683      * <p>A caller can never increase the maximum priority of a thread group.
684      * Such an attempt will not result in an exception, it will
685      * simply leave the thread group with its current maximum priority.
686      *
687      * @param newMax the new maximum priority to be set
688      *
689      * @throws SecurityException if {@code checkAccess()} fails with a
690      *         SecurityException
691      * @throws IllegalArgumentException if the new priority is greater than
692      *         Thread.MAX_PRIORITY or less than Thread.MIN_PRIORITY
693      *
694      * @see #getMaxPriority
695      */
setMaxPriority(int newMax)696     public final void setMaxPriority(int newMax) {
697         checkAccess();
698 
699         if (newMax <= this.maxPriority) {
700             if (newMax < Thread.MIN_PRIORITY) {
701                 newMax = Thread.MIN_PRIORITY;
702             }
703 
704             int parentPriority = parent == null ? newMax : parent.getMaxPriority();
705             this.maxPriority = parentPriority <= newMax ? parentPriority : newMax;
706             // Lock this subpart of the tree as we walk
707             synchronized (this.childrenGroupsLock) {
708                 // ??? why not maxPriority
709                 for (int i = 0; i < numGroups; i++) {
710                     this.childrenGroups[i].setMaxPriority(newMax);
711                 }
712             }
713         }
714     }
715 
716     /**
717      * Sets the parent {@code ThreadGroup} of this thread group, and adds this
718      * thread group to the parent's collection of immediate children (if {@code parent} is
719      * not {@code null}).
720      *
721      * @param parent The parent ThreadGroup, or null to make this thread group
722      *        the root ThreadGroup
723      *
724      * @see #getParent
725      * @see #parentOf
726      */
setParent(ThreadGroup parent)727     private void setParent(ThreadGroup parent) {
728         if (parent != null) {
729             parent.add(this);
730         }
731         this.parent = parent;
732     }
733 
734     /**
735      * Stops every thread in this group and recursively in all its subgroups.
736      *
737      * @throws SecurityException if {@code this.checkAccess()} fails with
738      *         a SecurityException
739      *
740      * @see Thread#stop()
741      * @see Thread#stop(Throwable)
742      * @see ThreadDeath
743      *
744      * @deprecated Requires deprecated method Thread.stop().
745      */
746     @SuppressWarnings("deprecation")
747     @Deprecated
stop()748     public final void stop() {
749         if (stopHelper()) {
750             Thread.currentThread().stop();
751         }
752     }
753 
754     /**
755      * @deprecated Requires deprecated method Thread.suspend().
756      */
757     @SuppressWarnings("deprecation")
758     @Deprecated
stopHelper()759     private final boolean stopHelper() {
760         checkAccess();
761 
762         boolean stopCurrent = false;
763         // Lock this subpart of the tree as we walk
764         synchronized (this.childrenThreadsLock) {
765             Thread current = Thread.currentThread();
766             for (int i = 0; i < numThreads; i++) {
767                 if (this.childrenThreads[i] == current) {
768                     stopCurrent = true;
769                 } else {
770                     this.childrenThreads[i].stop();
771                 }
772             }
773         }
774         // Lock this subpart of the tree as we walk
775         synchronized (this.childrenGroupsLock) {
776             for (int i = 0; i < numGroups; i++) {
777                 stopCurrent |= this.childrenGroups[i].stopHelper();
778             }
779         }
780         return stopCurrent;
781     }
782 
783     /**
784      * Suspends every thread in this group and recursively in all its
785      * subgroups.
786      *
787      * @throws SecurityException if {@code this.checkAccess()} fails with
788      *         a SecurityException
789      *
790      * @see Thread#suspend
791      * @see #resume
792      *
793      * @deprecated Requires deprecated method Thread.suspend().
794      */
795     @SuppressWarnings("deprecation")
796     @Deprecated
suspend()797     public final void suspend() {
798         if (suspendHelper()) {
799             Thread.currentThread().suspend();
800         }
801     }
802 
803     /**
804      * @deprecated Requires deprecated method Thread.suspend().
805      */
806     @SuppressWarnings("deprecation")
807     @Deprecated
suspendHelper()808     private final boolean suspendHelper() {
809         checkAccess();
810 
811         boolean suspendCurrent = false;
812         // Lock this subpart of the tree as we walk
813         synchronized (this.childrenThreadsLock) {
814             Thread current = Thread.currentThread();
815             for (int i = 0; i < numThreads; i++) {
816                 if (this.childrenThreads[i] == current) {
817                     suspendCurrent = true;
818                 } else {
819                     this.childrenThreads[i].suspend();
820                 }
821             }
822         }
823         // Lock this subpart of the tree as we walk
824         synchronized (this.childrenGroupsLock) {
825             for (int i = 0; i < numGroups; i++) {
826                 suspendCurrent |= this.childrenGroups[i].suspendHelper();
827             }
828         }
829         return suspendCurrent;
830     }
831 
832     @Override
toString()833     public String toString() {
834         return getClass().getName() + "[name=" + this.getName() + ",maxPriority="
835                 + this.getMaxPriority() + "]";
836     }
837 
838     /**
839      * Handles uncaught exceptions. Any uncaught exception in any {@code Thread}
840      * is forwarded to the thread's {@code ThreadGroup} by invoking this
841      * method.
842      *
843      * <p>New code should use {@link Thread#setUncaughtExceptionHandler} instead of thread groups.
844      *
845      * @param t the Thread that terminated with an uncaught exception
846      * @param e the uncaught exception itself
847      */
uncaughtException(Thread t, Throwable e)848     public void uncaughtException(Thread t, Throwable e) {
849         // BEGIN android-changed
850         if (parent != null) {
851             parent.uncaughtException(t, e);
852         } else if (Thread.getDefaultUncaughtExceptionHandler() != null) {
853             // TODO The spec is unclear regarding this. What do we do?
854             Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, e);
855         } else if (!(e instanceof ThreadDeath)) {
856             // No parent group, has to be 'system' Thread Group
857             e.printStackTrace(System.err);
858         }
859         // END android-changed
860     }
861 
862     // BEGIN android-added
863     /**
864      * Non-standard method for adding a thread to a group, required by Dalvik.
865      *
866      * @param thread Thread to add
867      *
868      * @throws IllegalThreadStateException if the thread has been destroyed
869      *         already
870      *
871      * @see #add(java.lang.Thread)
872      * @see #removeThread(java.lang.Thread)
873      */
addThread(Thread thread)874     void addThread(Thread thread) throws IllegalThreadStateException {
875         add(thread);
876     }
877 
878     /**
879      * Non-standard method for adding a thread to a group, required by Dalvik.
880      *
881      * @param thread Thread to add
882      *
883      * @throws IllegalThreadStateException if the thread has been destroyed
884      *         already
885      *
886      * @see #remove(java.lang.Thread)
887      * @see #addThread(java.lang.Thread)
888      */
removeThread(Thread thread)889     void removeThread(Thread thread) throws IllegalThreadStateException {
890         remove(thread);
891     }
892     // END android-added
893 }
894