All Downloads are FREE. Search and download functionalities are using the official Maven repository.

java.lang.ThreadGroup Maven / Gradle / Ivy

There is a newer version: 1.14.0
Show newest version
/*
 * 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;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import libcore.util.CollectionUtils;

/**
 * {@code ThreadGroup} is a means of organizing threads into a hierarchical structure.
 * This class is obsolete. See Effective Java Item 73, "Avoid thread groups" for details.
 * @see Thread
 */
public class ThreadGroup implements Thread.UncaughtExceptionHandler {

    // Name of this ThreadGroup
    // VM needs this field name for debugging.
    private String name;

    // Maximum priority for Threads inside this ThreadGroup
    private int maxPriority = Thread.MAX_PRIORITY;

    // The ThreadGroup to which this ThreadGroup belongs
    // VM needs this field name for debugging.
    final ThreadGroup parent;

    /**
     * Weak references to the threads in this group.
     * Access is guarded by synchronizing on this field.
     */
    private final List> threadRefs = new ArrayList>(5);

    /**
     * View of the threads.
     * Access is guarded by synchronizing on threadRefs.
     */
    private final Iterable threads = CollectionUtils.dereferenceIterable(threadRefs, true);

    /**
     * Thread groups. Access is guarded by synchronizing on this field.
     */
    private final List groups = new ArrayList(3);

    // Whether this ThreadGroup is a daemon ThreadGroup or not
    private boolean isDaemon;

    // Whether this ThreadGroup has already been destroyed or not
    private boolean isDestroyed;

    /* the VM uses these directly; do not rename */
    //static final ThreadGroup mSystem = new ThreadGroup();
    static final ThreadGroup mMain = new ThreadGroup(); //mSystem, "main");

    /**
     * Constructs a new {@code ThreadGroup} with the given name. The new {@code ThreadGroup}
     * will be child of the {@code ThreadGroup} to which the calling thread belongs.
     *
     * @param name the name
     * @see Thread#currentThread
     */
    public ThreadGroup(String name) {
        this(Thread.currentThread().getThreadGroup(), name);
    }

    /**
     * Constructs a new {@code ThreadGroup} with the given name, as a child of the
     * given {@code ThreadGroup}.
     *
     * @param parent the parent
     * @param name the name
     * @throws NullPointerException if {@code parent == null}
     * @throws IllegalThreadStateException if {@code parent} has been
     *         destroyed already
     */
    public ThreadGroup(ThreadGroup parent, String name) {
        if (parent == null) {
            throw new NullPointerException("parent == null");
        }
        this.name = name;
        this.parent = parent;
        if (parent != null) {
            parent.add(this);
            this.setMaxPriority(parent.getMaxPriority());
            if (parent.isDaemon()) {
                this.setDaemon(true);
            }
        }
    }

    /**
     * Initialize the special "system" ThreadGroup. Was "main" in Harmony,
     * but we have an additional group above that in Android.
     */
    private ThreadGroup() {
        //this.name = "system";
        this.name = "main";
        this.parent = null;
    }

    /**
     * Returns the number of running {@code Thread}s which are children of this thread group,
     * directly or indirectly.
     *
     * @return the number of children
     */
    public int activeCount() {
        int count = 0;
        synchronized (threadRefs) {
            for (Thread thread : threads) {
                if (thread.isAlive()) {
                    count++;
                }
            }
        }
        synchronized (groups) {
            for (ThreadGroup group : groups) {
                count += group.activeCount();
            }
        }
        return count;
    }

    /**
     * Returns the number of {@code ThreadGroup}s which are children of this group,
     * directly or indirectly.
     *
     * @return the number of children
     */
    public int activeGroupCount() {
        int count = 0;
        synchronized (groups) {
            for (ThreadGroup group : groups) {
                // One for this group & the subgroups
                count += 1 + group.activeGroupCount();
            }
        }
        return count;
    }

    /**
     * Adds a {@code ThreadGroup} to this thread group.
     *
     * @param g ThreadGroup to add
     * @throws IllegalThreadStateException if this group has been destroyed already
     */
    private void add(ThreadGroup g) throws IllegalThreadStateException {
        synchronized (groups) {
            if (isDestroyed) {
                throw new IllegalThreadStateException();
            }
            groups.add(g);
        }
    }

    /**
     * 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 {@link #suspend()}.
     */
    @Deprecated
    public boolean allowThreadSuspension(boolean b) {
        // Does not apply to this VM, no-op
        return true;
    }

    /**
     * Does nothing.
     */
    public final void checkAccess() {
    }

    /**
     * Destroys this thread group and recursively all its subgroups. It is only legal
     * to destroy a {@code ThreadGroup} that has no threads in it. Any daemon
     * {@code ThreadGroup} is destroyed automatically when it becomes empty (no threads
     * or thread groups in it).
     *
     * @throws IllegalThreadStateException if this thread group or any of its
     *         subgroups has been destroyed already or if it still contains
     *         threads.
     */
    public final void destroy() {
        synchronized (threadRefs) {
            synchronized (groups) {
                if (isDestroyed) {
                    throw new IllegalThreadStateException(
                            "Thread group was already destroyed: "
                            + (this.name != null ? this.name : "n/a"));
                }
                if (threads.iterator().hasNext()) {
                    throw new IllegalThreadStateException(
                            "Thread group still contains threads: "
                            + (this.name != null ? this.name : "n/a"));
                }
                // Call recursively for subgroups
                while (!groups.isEmpty()) {
                    // We always get the first element - remember, when the
                    // child dies it removes itself from our collection. See
                    // below.
                    groups.get(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 this thread group and recursively all its
     * subgroups if this is a daemon ThreadGroup.
     *
     * @see #destroy
     * @see #setDaemon
     * @see #isDaemon
     */
    private void destroyIfEmptyDaemon() {
        // Has to be non-destroyed daemon to make sense
        synchronized (threadRefs) {
            if (isDaemon && !isDestroyed && !threads.iterator().hasNext()) {
                synchronized (groups) {
                    if (groups.isEmpty()) {
                        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.
     *
     * 

Note that this method will silently ignore any threads that don't fit in the * supplied array. * * @param threads the array into which the {@code Thread}s will be copied * @return the number of {@code Thread}s 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. * *

Note that this method will silently ignore any threads that don't fit in the * supplied array. * * @param threads the array into which the {@code Thread}s will be copied * @param recurse indicates whether {@code Thread}s in subgroups should be * recursively copied as well * @return the number of {@code Thread}s 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. * *

Note that this method will silently ignore any thread groups that don't fit in the * supplied array. * * @param groups the array into which the {@code ThreadGroup}s will be copied * @return the number of {@code ThreadGroup}s 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 stores the groups in the given array. Returns when * the array is full or no more groups remain, whichever happens first. * *

Note that this method will silently ignore any thread groups that don't fit in the * supplied array. * * @param groups the array into which the {@code ThreadGroup}s will be copied * @param recurse indicates whether {@code ThreadGroup}s in subgroups should be * recursively copied as well or not * @return the number of {@code ThreadGroup}s that were copied */ public int enumerate(ThreadGroup[] groups, boolean recurse) { return enumerateGeneric(groups, recurse, 0, false); } /** * Copies into enumeration starting at * enumerationIndex all Threads or ThreadGroups in the * receiver. If recurse 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) { if (enumeratingThreads) { synchronized (threadRefs) { // walk the references directly so we can iterate in reverse order for (int i = threadRefs.size() - 1; i >= 0; --i) { Thread thread = threadRefs.get(i).get(); if (thread != null && thread.isAlive()) { if (enumerationIndex >= enumeration.length) { return enumerationIndex; } enumeration[enumerationIndex++] = thread; } } } } else { synchronized (groups) { for (int i = groups.size() - 1; i >= 0; --i) { if (enumerationIndex >= enumeration.length) { return enumerationIndex; } enumeration[enumerationIndex++] = groups.get(i); } } } if (recurse) { synchronized (groups) { for (ThreadGroup group : groups) { if (enumerationIndex >= enumeration.length) { return enumerationIndex; } enumerationIndex = group.enumerateGeneric(enumeration, recurse, enumerationIndex, enumeratingThreads); } } } return enumerationIndex; } /** * Returns the maximum allowed priority for a {@code Thread} in this thread group. * * @return the maximum priority * * @see #setMaxPriority */ public final int getMaxPriority() { return maxPriority; } /** * Returns the name of this thread group. * * @return the group's name */ public final String getName() { return name; } /** * Returns this thread group's parent {@code ThreadGroup}. It can be null if this * is the the root ThreadGroup. * * @return the parent */ public final ThreadGroup getParent() { return parent; } /** * Interrupts every {@code Thread} in this group and recursively in all its * subgroups. * * @see Thread#interrupt */ public final void interrupt() { synchronized (threadRefs) { for (Thread thread : threads) { thread.interrupt(); } } synchronized (groups) { for (ThreadGroup group : groups) { group.interrupt(); } } } /** * Checks whether this thread group is a daemon {@code ThreadGroup}. * * @return true if this thread group is a daemon {@code ThreadGroup} * * @see #setDaemon * @see #destroy */ public final boolean isDaemon() { return isDaemon; } /** * Checks whether this thread group has already been destroyed. * * @return true if this thread group has already been destroyed * @see #destroy */ public synchronized boolean isDestroyed() { return isDestroyed; } /** * Outputs to {@code System.out} a text representation of the * hierarchy of {@code Thread}s and {@code ThreadGroup}s in this thread group (and recursively). * Proper indentation is used to show 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 this thread group (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) { indent(levels); System.out.println(this.toString()); ++levels; synchronized (threadRefs) { for (Thread thread : threads) { indent(levels); System.out.println(thread); } } synchronized (groups) { for (ThreadGroup group : groups) { group.list(levels); } } } private void indent(int levels) { for (int i = 0; i < levels; i++) { System.out.print(" "); // 4 spaces for each level } } /** * Checks whether this thread group is a direct or indirect parent group of a * given {@code ThreadGroup}. * * @param g the potential child {@code ThreadGroup} * @return true if this thread group 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 an immediate subgroup. * * @param g ThreadGroup to remove * * @see #add(Thread) * @see #add(ThreadGroup) */ private void remove(ThreadGroup g) { synchronized (groups) { for (Iterator i = groups.iterator(); i.hasNext(); ) { ThreadGroup threadGroup = i.next(); if (threadGroup.equals(g)) { i.remove(); break; } } } destroyIfEmptyDaemon(); } /** * Resumes every thread in this group and recursively in all its * subgroups. * * @see Thread#resume * @see #suspend * * @deprecated Requires deprecated method {@link Thread#resume()}. */ @SuppressWarnings("deprecation") @Deprecated public final void resume() { synchronized (threadRefs) { for (Thread thread : threads) { thread.resume(); } } synchronized (groups) { for (ThreadGroup group : groups) { group.resume(); } } } /** * Sets whether this is a daemon {@code ThreadGroup} or not. Daemon * thread groups are automatically destroyed when they become empty. * * @param isDaemon the new value * @see #isDaemon * @see #destroy */ public final void setDaemon(boolean isDaemon) { this.isDaemon = isDaemon; } /** * Configures the maximum allowed priority for a {@code Thread} in this group and * recursively in all its subgroups. * *

A caller can never increase the maximum priority of a thread group. * Such an attempt will not result in an exception, it will * simply leave the thread group with its current maximum priority. * * @param newMax the new maximum priority to be set * * @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) { 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; synchronized (groups) { for (ThreadGroup group : groups) { group.setMaxPriority(newMax); } } } } /** * Stops every thread in this group and recursively in all its subgroups. * * @see Thread#stop() * @see Thread#stop(Throwable) * @see ThreadDeath * * @deprecated Requires deprecated method {@link Thread#stop()}. */ @SuppressWarnings("deprecation") @Deprecated public final void stop() { if (stopHelper()) { Thread.currentThread().stop(); } } @SuppressWarnings("deprecation") private boolean stopHelper() { boolean stopCurrent = false; synchronized (threadRefs) { Thread current = Thread.currentThread(); for (Thread thread : threads) { if (thread == current) { stopCurrent = true; } else { thread.stop(); } } } synchronized (groups) { for (ThreadGroup group : groups) { stopCurrent |= group.stopHelper(); } } return stopCurrent; } /** * Suspends every thread in this group and recursively in all its * subgroups. * * @see Thread#suspend * @see #resume * * @deprecated Requires deprecated method {@link Thread#suspend()}. */ @SuppressWarnings("deprecation") @Deprecated public final void suspend() { if (suspendHelper()) { Thread.currentThread().suspend(); } } @SuppressWarnings("deprecation") private boolean suspendHelper() { boolean suspendCurrent = false; synchronized (threadRefs) { Thread current = Thread.currentThread(); for (Thread thread : threads) { if (thread == current) { suspendCurrent = true; } else { thread.suspend(); } } } synchronized (groups) { for (ThreadGroup group : groups) { suspendCurrent |= group.suspendHelper(); } } return suspendCurrent; } @Override public String toString() { return getClass().getName() + "[name=" + getName() + ",maxPriority=" + getMaxPriority() + "]"; } /** * Handles uncaught exceptions. Any uncaught exception in any {@code Thread} * is forwarded to the thread's {@code ThreadGroup} by invoking this * method. * *

New code should use {@link Thread#setUncaughtExceptionHandler} instead of thread groups. * * @param t the Thread that terminated with an uncaught exception * @param e the uncaught exception itself */ public void uncaughtException(Thread t, Throwable e) { if (parent != null) { parent.uncaughtException(t, e); } else if (Thread.getDefaultUncaughtExceptionHandler() != null) { // TODO The spec is unclear regarding this. What do we do? Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, e); } else if (!(e instanceof ThreadDeath)) { // No parent group, has to be 'system' Thread Group e.printStackTrace(System.err); } } /** * Called by the Thread constructor. */ final void addThread(Thread thread) throws IllegalThreadStateException { synchronized (threadRefs) { if (isDestroyed) { throw new IllegalThreadStateException(); } threadRefs.add(new WeakReference(thread)); } } /** * Called by the VM when a Thread dies. */ final void removeThread(Thread thread) throws IllegalThreadStateException { synchronized (threadRefs) { for (Iterator i = threads.iterator(); i.hasNext(); ) { if (i.next().equals(thread)) { i.remove(); break; } } } destroyIfEmptyDaemon(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy