Frames | No Frames |
1: /* ThreadGroup -- a group of Threads 2: Copyright (C) 1998, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package java.lang; 39: 40: import java.lang.Thread.UncaughtExceptionHandler; 41: import java.util.Vector; 42: 43: /** 44: * ThreadGroup allows you to group Threads together. There is a hierarchy 45: * of ThreadGroups, and only the initial ThreadGroup has no parent. A Thread 46: * may access information about its own ThreadGroup, but not its parents or 47: * others outside the tree. 48: * 49: * @author John Keiser 50: * @author Tom Tromey 51: * @author Bryce McKinlay 52: * @author Eric Blake (ebb9@email.byu.edu) 53: * @see Thread 54: * @since 1.0 55: * @status updated to 1.4 56: */ 57: public class ThreadGroup implements UncaughtExceptionHandler 58: { 59: /** The Initial, top-level ThreadGroup. */ 60: static ThreadGroup root = new ThreadGroup(); 61: 62: /** 63: * This flag is set if an uncaught exception occurs. The runtime should 64: * check this and exit with an error status if it is set. 65: */ 66: static boolean had_uncaught_exception; 67: 68: /** The parent thread group. */ 69: final ThreadGroup parent; 70: 71: /** The group name, non-null. */ 72: final String name; 73: 74: /** The threads in the group. */ 75: private final Vector threads = new Vector(); 76: 77: /** Child thread groups, or null when this group is destroyed. */ 78: private Vector groups = new Vector(); 79: 80: /** If all threads in the group are daemons. */ 81: private boolean daemon_flag = false; 82: 83: /** The maximum group priority. */ 84: private int maxpri; 85: 86: /** 87: * Hidden constructor to build the root node. 88: */ 89: private ThreadGroup() 90: { 91: name = "main"; 92: parent = null; 93: maxpri = Thread.MAX_PRIORITY; 94: } 95: 96: /** 97: * Create a new ThreadGroup using the given name and the current thread's 98: * ThreadGroup as a parent. There may be a security check, 99: * <code>checkAccess</code>. 100: * 101: * @param name the name to use for the ThreadGroup 102: * @throws SecurityException if the current thread cannot create a group 103: * @see #checkAccess() 104: */ 105: public ThreadGroup(String name) 106: { 107: this(Thread.currentThread().group, name); 108: } 109: 110: /** 111: * Create a new ThreadGroup using the given name and parent group. The new 112: * group inherits the maximum priority and daemon status of its parent 113: * group. There may be a security check, <code>checkAccess</code>. 114: * 115: * @param name the name to use for the ThreadGroup 116: * @param parent the ThreadGroup to use as a parent 117: * @throws NullPointerException if parent is null 118: * @throws SecurityException if the current thread cannot create a group 119: * @throws IllegalThreadStateException if the parent is destroyed 120: * @see #checkAccess() 121: */ 122: public ThreadGroup(ThreadGroup parent, String name) 123: { 124: parent.checkAccess(); 125: this.parent = parent; 126: this.name = name; 127: maxpri = parent.maxpri; 128: daemon_flag = parent.daemon_flag; 129: synchronized (parent) 130: { 131: if (parent.groups == null) 132: throw new IllegalThreadStateException(); 133: parent.groups.add(this); 134: } 135: } 136: 137: /** 138: * Get the name of this ThreadGroup. 139: * 140: * @return the name of this ThreadGroup 141: */ 142: public final String getName() 143: { 144: return name; 145: } 146: 147: /** 148: * Get the parent of this ThreadGroup. If the parent is not null, there 149: * may be a security check, <code>checkAccess</code>. 150: * 151: * @return the parent of this ThreadGroup 152: * @throws SecurityException if permission is denied 153: */ 154: public final ThreadGroup getParent() 155: { 156: if (parent != null) 157: parent.checkAccess(); 158: return parent; 159: } 160: 161: /** 162: * Get the maximum priority of Threads in this ThreadGroup. Threads created 163: * after this call in this group may not exceed this priority. 164: * 165: * @return the maximum priority of Threads in this ThreadGroup 166: */ 167: public final int getMaxPriority() 168: { 169: return maxpri; 170: } 171: 172: /** 173: * Tell whether this ThreadGroup is a daemon group. A daemon group will 174: * be automatically destroyed when its last thread is stopped and 175: * its last thread group is destroyed. 176: * 177: * @return whether this ThreadGroup is a daemon group 178: */ 179: public final boolean isDaemon() 180: { 181: return daemon_flag; 182: } 183: 184: /** 185: * Tell whether this ThreadGroup has been destroyed or not. 186: * 187: * @return whether this ThreadGroup has been destroyed or not 188: * @since 1.1 189: */ 190: public synchronized boolean isDestroyed() 191: { 192: return groups == null; 193: } 194: 195: /** 196: * Set whether this ThreadGroup is a daemon group. A daemon group will be 197: * destroyed when its last thread is stopped and its last thread group is 198: * destroyed. There may be a security check, <code>checkAccess</code>. 199: * 200: * @param daemon whether this ThreadGroup should be a daemon group 201: * @throws SecurityException if you cannot modify this ThreadGroup 202: * @see #checkAccess() 203: */ 204: public final void setDaemon(boolean daemon) 205: { 206: checkAccess(); 207: daemon_flag = daemon; 208: } 209: 210: /** 211: * Set the maximum priority for Threads in this ThreadGroup. setMaxPriority 212: * can only be used to reduce the current maximum. If maxpri is greater 213: * than the current Maximum of the parent group, the current value is not 214: * changed. Otherwise, all groups which belong to this have their priority 215: * adjusted as well. Calling this does not affect threads already in this 216: * ThreadGroup. There may be a security check, <code>checkAccess</code>. 217: * 218: * @param maxpri the new maximum priority for this ThreadGroup 219: * @throws SecurityException if you cannot modify this ThreadGroup 220: * @see #getMaxPriority() 221: * @see #checkAccess() 222: */ 223: public final synchronized void setMaxPriority(int maxpri) 224: { 225: checkAccess(); 226: if (maxpri < Thread.MIN_PRIORITY || maxpri > Thread.MAX_PRIORITY) 227: return; 228: if (parent != null && maxpri > parent.maxpri) 229: maxpri = parent.maxpri; 230: this.maxpri = maxpri; 231: if (groups == null) 232: return; 233: int i = groups.size(); 234: while (--i >= 0) 235: ((ThreadGroup) groups.get(i)).setMaxPriority(maxpri); 236: } 237: 238: /** 239: * Check whether this ThreadGroup is an ancestor of the specified 240: * ThreadGroup, or if they are the same. 241: * 242: * @param group the group to test on 243: * @return whether this ThreadGroup is a parent of the specified group 244: */ 245: public final boolean parentOf(ThreadGroup group) 246: { 247: while (group != null) 248: { 249: if (group == this) 250: return true; 251: group = group.parent; 252: } 253: return false; 254: } 255: 256: /** 257: * Find out if the current Thread can modify this ThreadGroup. This passes 258: * the check on to <code>SecurityManager.checkAccess(this)</code>. 259: * 260: * @throws SecurityException if the current Thread cannot modify this 261: * ThreadGroup 262: * @see SecurityManager#checkAccess(ThreadGroup) 263: */ 264: public final void checkAccess() 265: { 266: // Bypass System.getSecurityManager, for bootstrap efficiency. 267: SecurityManager sm = SecurityManager.current; 268: if (sm != null) 269: sm.checkAccess(this); 270: } 271: 272: /** 273: * Return an estimate of the total number of active threads in this 274: * ThreadGroup and all its descendants. This cannot return an exact number, 275: * since the status of threads may change after they were counted; but it 276: * should be pretty close. Based on a JDC bug, 277: * <a href="http://developer.java.sun.com/developer/bugParade/bugs/4089701.html"> 278: * 4089701</a>, we take active to mean isAlive(). 279: * 280: * @return count of active threads in this ThreadGroup and its descendants 281: */ 282: public int activeCount() 283: { 284: int total = 0; 285: if (groups == null) 286: return total; 287: int i = threads.size(); 288: while (--i >= 0) 289: if (((Thread) threads.get(i)).isAlive()) 290: total++; 291: i = groups.size(); 292: while (--i >= 0) 293: total += ((ThreadGroup) groups.get(i)).activeCount(); 294: return total; 295: } 296: 297: /** 298: * Copy all of the active Threads from this ThreadGroup and its descendants 299: * into the specified array. If the array is not big enough to hold all 300: * the Threads, extra Threads will simply not be copied. There may be a 301: * security check, <code>checkAccess</code>. 302: * 303: * @param array the array to put the threads into 304: * @return the number of threads put into the array 305: * @throws SecurityException if permission was denied 306: * @throws NullPointerException if array is null 307: * @throws ArrayStoreException if a thread does not fit in the array 308: * @see #activeCount() 309: * @see #checkAccess() 310: * @see #enumerate(Thread[], boolean) 311: */ 312: public int enumerate(Thread[] array) 313: { 314: return enumerate(array, 0, true); 315: } 316: 317: /** 318: * Copy all of the active Threads from this ThreadGroup and, if desired, 319: * from its descendants, into the specified array. If the array is not big 320: * enough to hold all the Threads, extra Threads will simply not be copied. 321: * There may be a security check, <code>checkAccess</code>. 322: * 323: * @param array the array to put the threads into 324: * @param recurse whether to recurse into descendent ThreadGroups 325: * @return the number of threads put into the array 326: * @throws SecurityException if permission was denied 327: * @throws NullPointerException if array is null 328: * @throws ArrayStoreException if a thread does not fit in the array 329: * @see #activeCount() 330: * @see #checkAccess() 331: */ 332: public int enumerate(Thread[] array, boolean recurse) 333: { 334: return enumerate(array, 0, recurse); 335: } 336: 337: /** 338: * Get the number of active groups in this ThreadGroup. This group itself 339: * is not included in the count. A sub-group is active if it has not been 340: * destroyed. This cannot return an exact number, since the status of 341: * threads may change after they were counted; but it should be pretty close. 342: * 343: * @return the number of active groups in this ThreadGroup 344: */ 345: public int activeGroupCount() 346: { 347: if (groups == null) 348: return 0; 349: int total = groups.size(); 350: int i = total; 351: while (--i >= 0) 352: total += ((ThreadGroup) groups.get(i)).activeGroupCount(); 353: return total; 354: } 355: 356: /** 357: * Copy all active ThreadGroups that are descendants of this ThreadGroup 358: * into the specified array. If the array is not large enough to hold all 359: * active ThreadGroups, extra ThreadGroups simply will not be copied. There 360: * may be a security check, <code>checkAccess</code>. 361: * 362: * @param array the array to put the ThreadGroups into 363: * @return the number of ThreadGroups copied into the array 364: * @throws SecurityException if permission was denied 365: * @throws NullPointerException if array is null 366: * @throws ArrayStoreException if a group does not fit in the array 367: * @see #activeCount() 368: * @see #checkAccess() 369: * @see #enumerate(ThreadGroup[], boolean) 370: */ 371: public int enumerate(ThreadGroup[] array) 372: { 373: return enumerate(array, 0, true); 374: } 375: 376: /** 377: * Copy all active ThreadGroups that are children of this ThreadGroup into 378: * the specified array, and if desired, also all descendents. If the array 379: * is not large enough to hold all active ThreadGroups, extra ThreadGroups 380: * simply will not be copied. There may be a security check, 381: * <code>checkAccess</code>. 382: * 383: * @param array the array to put the ThreadGroups into 384: * @param recurse whether to recurse into descendent ThreadGroups 385: * @return the number of ThreadGroups copied into the array 386: * @throws SecurityException if permission was denied 387: * @throws NullPointerException if array is null 388: * @throws ArrayStoreException if a group does not fit in the array 389: * @see #activeCount() 390: * @see #checkAccess() 391: */ 392: public int enumerate(ThreadGroup[] array, boolean recurse) 393: { 394: return enumerate(array, 0, recurse); 395: } 396: 397: /** 398: * Stop all Threads in this ThreadGroup and its descendants. 399: * 400: * <p>This is inherently unsafe, as it can interrupt synchronized blocks and 401: * leave data in bad states. Hence, there is a security check: 402: * <code>checkAccess()</code>, followed by further checks on each thread 403: * being stopped. 404: * 405: * @throws SecurityException if permission is denied 406: * @see #checkAccess() 407: * @see Thread#stop(Throwable) 408: * @deprecated unsafe operation, try not to use 409: */ 410: public final synchronized void stop() 411: { 412: checkAccess(); 413: if (groups == null) 414: return; 415: int i = threads.size(); 416: while (--i >= 0) 417: ((Thread) threads.get(i)).stop(); 418: i = groups.size(); 419: while (--i >= 0) 420: ((ThreadGroup) groups.get(i)).stop(); 421: } 422: 423: /** 424: * Interrupt all Threads in this ThreadGroup and its sub-groups. There may 425: * be a security check, <code>checkAccess</code>. 426: * 427: * @throws SecurityException if permission is denied 428: * @see #checkAccess() 429: * @see Thread#interrupt() 430: * @since 1.2 431: */ 432: public final synchronized void interrupt() 433: { 434: checkAccess(); 435: if (groups == null) 436: return; 437: int i = threads.size(); 438: while (--i >= 0) 439: ((Thread) threads.get(i)).interrupt(); 440: i = groups.size(); 441: while (--i >= 0) 442: ((ThreadGroup) groups.get(i)).interrupt(); 443: } 444: 445: /** 446: * Suspend all Threads in this ThreadGroup and its descendants. 447: * 448: * <p>This is inherently unsafe, as suspended threads still hold locks, 449: * which can lead to deadlock. Hence, there is a security check: 450: * <code>checkAccess()</code>, followed by further checks on each thread 451: * being suspended. 452: * 453: * @throws SecurityException if permission is denied 454: * @see #checkAccess() 455: * @see Thread#suspend() 456: * @deprecated unsafe operation, try not to use 457: */ 458: public final synchronized void suspend() 459: { 460: checkAccess(); 461: if (groups == null) 462: return; 463: int i = threads.size(); 464: while (--i >= 0) 465: ((Thread) threads.get(i)).suspend(); 466: i = groups.size(); 467: while (--i >= 0) 468: ((ThreadGroup) groups.get(i)).suspend(); 469: } 470: 471: /** 472: * Resume all suspended Threads in this ThreadGroup and its descendants. 473: * To mirror suspend(), there is a security check: 474: * <code>checkAccess()</code>, followed by further checks on each thread 475: * being resumed. 476: * 477: * @throws SecurityException if permission is denied 478: * @see #checkAccess() 479: * @see Thread#suspend() 480: * @deprecated pointless, since suspend is deprecated 481: */ 482: public final synchronized void resume() 483: { 484: checkAccess(); 485: if (groups == null) 486: return; 487: int i = threads.size(); 488: while (--i >= 0) 489: ((Thread) threads.get(i)).resume(); 490: i = groups.size(); 491: while (--i >= 0) 492: ((ThreadGroup) groups.get(i)).resume(); 493: } 494: 495: /** 496: * Destroy this ThreadGroup. The group must be empty, meaning that all 497: * threads and sub-groups have completed execution. Daemon groups are 498: * destroyed automatically. There may be a security check, 499: * <code>checkAccess</code>. 500: * 501: * @throws IllegalThreadStateException if the ThreadGroup is not empty, or 502: * was previously destroyed 503: * @throws SecurityException if permission is denied 504: * @see #checkAccess() 505: */ 506: public final synchronized void destroy() 507: { 508: checkAccess(); 509: if (! threads.isEmpty() || groups == null) 510: throw new IllegalThreadStateException(); 511: int i = groups.size(); 512: while (--i >= 0) 513: ((ThreadGroup) groups.get(i)).destroy(); 514: groups = null; 515: if (parent != null) 516: parent.removeGroup(this); 517: } 518: 519: /** 520: * Print out information about this ThreadGroup to System.out. This is 521: * meant for debugging purposes. <b>WARNING:</b> This method is not secure, 522: * and can print the name of threads to standard out even when you cannot 523: * otherwise get at such threads. 524: */ 525: public void list() 526: { 527: list(""); 528: } 529: 530: /** 531: * When a Thread in this ThreadGroup does not catch an exception, the 532: * virtual machine calls this method. The default implementation simply 533: * passes the call to the parent; then in top ThreadGroup, it will 534: * ignore ThreadDeath and print the stack trace of any other throwable. 535: * Override this method if you want to handle the exception in a different 536: * manner. 537: * 538: * @param thread the thread that exited 539: * @param t the uncaught throwable 540: * @throws NullPointerException if t is null 541: * @see ThreadDeath 542: * @see System#err 543: * @see Throwable#printStackTrace() 544: */ 545: public void uncaughtException(Thread thread, Throwable t) 546: { 547: if (parent != null) 548: parent.uncaughtException(thread, t); 549: else if (Thread.getDefaultUncaughtExceptionHandler() != null) 550: Thread.getDefaultUncaughtExceptionHandler().uncaughtException(thread, t); 551: else if (! (t instanceof ThreadDeath)) 552: { 553: if (t == null) 554: throw new NullPointerException(); 555: had_uncaught_exception = true; 556: try 557: { 558: if (thread != null) 559: System.err.print("Exception in thread \"" + thread.name + "\" "); 560: t.printStackTrace(System.err); 561: } 562: catch (Throwable x) 563: { 564: // This means that something is badly screwed up with the runtime, 565: // or perhaps someone overloaded the Throwable.printStackTrace to 566: // die. In any case, try to deal with it gracefully. 567: try 568: { 569: System.err.println(t); 570: System.err.println("*** Got " + x 571: + " while trying to print stack trace."); 572: } 573: catch (Throwable x2) 574: { 575: // Here, someone may have overloaded t.toString() or 576: // x.toString() to die. Give up all hope; we can't even chain 577: // the exception, because the chain would likewise die. 578: System.err.println("*** Catastrophic failure while handling " 579: + "uncaught exception."); 580: throw new InternalError(); 581: } 582: } 583: } 584: } 585: 586: /** 587: * Originally intended to tell the VM whether it may suspend Threads in 588: * low memory situations, this method was never implemented by Sun, and 589: * is hence a no-op. 590: * 591: * @param allow whether to allow low-memory thread suspension; ignored 592: * @return false 593: * @since 1.1 594: * @deprecated pointless, since suspend is deprecated 595: */ 596: public boolean allowThreadSuspension(boolean allow) 597: { 598: return false; 599: } 600: 601: /** 602: * Return a human-readable String representing this ThreadGroup. The format 603: * of the string is:<br> 604: * <code>getClass().getName() + "[name=" + getName() + ",maxpri=" 605: * + getMaxPriority() + ']'</code>. 606: * 607: * @return a human-readable String representing this ThreadGroup 608: */ 609: public String toString() 610: { 611: return getClass().getName() + "[name=" + name + ",maxpri=" + maxpri + ']'; 612: } 613: 614: /** 615: * Implements enumerate. 616: * 617: * @param list the array to put the threads into 618: * @param next the next open slot in the array 619: * @param recurse whether to recurse into descendent ThreadGroups 620: * @return the number of threads put into the array 621: * @throws SecurityException if permission was denied 622: * @throws NullPointerException if list is null 623: * @throws ArrayStoreException if a thread does not fit in the array 624: * @see #enumerate(Thread[]) 625: * @see #enumerate(Thread[], boolean) 626: */ 627: private int enumerate(Thread[] list, int next, boolean recurse) 628: { 629: checkAccess(); 630: if (groups == null) 631: return next; 632: int i = threads.size(); 633: while (--i >= 0 && next < list.length) 634: { 635: Thread t = (Thread) threads.get(i); 636: if (t.isAlive()) 637: list[next++] = t; 638: } 639: if (recurse) 640: { 641: i = groups.size(); 642: while (--i >= 0 && next < list.length) 643: { 644: ThreadGroup g = (ThreadGroup) groups.get(i); 645: next = g.enumerate(list, next, true); 646: } 647: } 648: return next; 649: } 650: 651: /** 652: * Implements enumerate. 653: * 654: * @param list the array to put the groups into 655: * @param next the next open slot in the array 656: * @param recurse whether to recurse into descendent ThreadGroups 657: * @return the number of groups put into the array 658: * @throws SecurityException if permission was denied 659: * @throws NullPointerException if list is null 660: * @throws ArrayStoreException if a group does not fit in the array 661: * @see #enumerate(ThreadGroup[]) 662: * @see #enumerate(ThreadGroup[], boolean) 663: */ 664: private int enumerate(ThreadGroup[] list, int next, boolean recurse) 665: { 666: checkAccess(); 667: if (groups == null) 668: return next; 669: int i = groups.size(); 670: while (--i >= 0 && next < list.length) 671: { 672: ThreadGroup g = (ThreadGroup) groups.get(i); 673: list[next++] = g; 674: if (recurse && next != list.length) 675: next = g.enumerate(list, next, true); 676: } 677: return next; 678: } 679: 680: /** 681: * Implements list. 682: * 683: * @param indentation the current level of indentation 684: * @see #list() 685: */ 686: private void list(String indentation) 687: { 688: if (groups == null) 689: return; 690: System.out.println(indentation + this); 691: indentation += " "; 692: int i = threads.size(); 693: while (--i >= 0) 694: System.out.println(indentation + threads.get(i)); 695: i = groups.size(); 696: while (--i >= 0) 697: ((ThreadGroup) groups.get(i)).list(indentation); 698: } 699: 700: /** 701: * Add a thread to the group. Called by Thread constructors. 702: * 703: * @param t the thread to add, non-null 704: * @throws IllegalThreadStateException if the group is destroyed 705: */ 706: final synchronized void addThread(Thread t) 707: { 708: if (groups == null) 709: throw new IllegalThreadStateException("ThreadGroup is destroyed"); 710: threads.add(t); 711: } 712: 713: /** 714: * Called by the VM to remove a thread that has died. 715: * 716: * @param t the thread to remove, non-null 717: * @XXX A ThreadListener to call this might be nice. 718: */ 719: final synchronized void removeThread(Thread t) 720: { 721: if (groups == null) 722: return; 723: threads.remove(t); 724: t.group = null; 725: // Daemon groups are automatically destroyed when all their threads die. 726: if (daemon_flag && groups.size() == 0 && threads.size() == 0) 727: { 728: // We inline destroy to avoid the access check. 729: groups = null; 730: if (parent != null) 731: parent.removeGroup(this); 732: } 733: } 734: 735: /** 736: * Called when a group is destroyed, to remove it from its parent. 737: * 738: * @param g the destroyed group, non-null 739: */ 740: final synchronized void removeGroup(ThreadGroup g) 741: { 742: groups.remove(g); 743: // Daemon groups are automatically destroyed when all their threads die. 744: if (daemon_flag && groups.size() == 0 && threads.size() == 0) 745: { 746: // We inline destroy to avoid the access check. 747: groups = null; 748: if (parent != null) 749: parent.removeGroup(this); 750: } 751: } 752: 753: /* 754: * Helper method for the VM. Find a Thread by its Id. 755: * 756: * @param id The Thread Id. 757: * @return Thread object or null if thread doesn't exist. 758: */ 759: static Thread getThreadFromId(long id) 760: { 761: return root.getThreadFromIdImpl(id); 762: } 763: 764: private Thread getThreadFromIdImpl(long id) 765: { 766: synchronized (threads) 767: { 768: for (int i = 0; i < threads.size(); i++) 769: { 770: Thread t = (Thread) threads.get(i); 771: if (t.getId() == id) 772: return t; 773: } 774: } 775: Vector groups = this.groups; 776: if (groups != null) 777: { 778: synchronized (groups) 779: { 780: for (int i = 0; i < groups.size(); i++) 781: { 782: ThreadGroup g = (ThreadGroup) groups.get(i); 783: Thread t = g.getThreadFromIdImpl(id); 784: if (t != null) 785: return t; 786: } 787: } 788: } 789: return null; 790: } 791: } // class ThreadGroup