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