ThreadLocal及内部类ThreadLocalMap源码

ThreadLocal类结构
其中ThreadLocalMap是静态内部类
ThreadLocal及内部类ThreadLocalMap源码_第1张图片
ThreadLocal类源码

package java.lang;
import java.lang.ref.*;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;

/**
 * ThreadLocal类提供线程私有的变量副本
 */
public class ThreadLocal<T> {
     
    /**
     * ThreadLocals rely on per-thread linear-probe hash maps attached
     * to each thread (Thread.threadLocals and
     * inheritableThreadLocals).  The ThreadLocal objects act as keys,
     * searched via threadLocalHashCode.  This is a custom hash code
     * (useful only within ThreadLocalMaps) that eliminates collisions
     * in the common case where consecutively constructed ThreadLocals
     * are used by the same threads, while remaining well-behaved in
     * less common cases.
     */
    private final int threadLocalHashCode = nextHashCode();

    /**
     * The next hash code to be given out. Updated atomically. Starts at
     * zero.
     */
    private static AtomicInteger nextHashCode =
        new AtomicInteger();

    /**
     * The difference between successively generated hash codes - turns
     * implicit sequential thread-local IDs into near-optimally spread
     * multiplicative hash values for power-of-two-sized tables.
     */
    private static final int HASH_INCREMENT = 0x61c88647;

    /**
     * Returns the next hash code.
     */
    private static int nextHashCode() {
     
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }

    /**
     * Returns the current thread's "initial value" for this
     * thread-local variable.  This method will be invoked the first
     * time a thread accesses the variable with the {@link #get}
     * method, unless the thread previously invoked the {@link #set}
     * method, in which case the {@code initialValue} method will not
     * be invoked for the thread.  Normally, this method is invoked at
     * most once per thread, but it may be invoked again in case of
     * subsequent invocations of {@link #remove} followed by {@link #get}.
     *
     */
    protected T initialValue() {
     
        return null;
    }

    /**
     * Creates a thread local variable. The initial value of the variable is
     * determined by invoking the {@code get} method on the {@code Supplier}.
     *
     * @param  the type of the thread local's value
     * @param supplier the supplier to be used to determine the initial value
     * @return a new thread local variable
     * @throws NullPointerException if the specified supplier is null
     * @since 1.8
     */
    public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
     
        return new SuppliedThreadLocal<>(supplier);
    }

    /**
     * Creates a thread local variable.
     * @see #withInitial(java.util.function.Supplier)
     */
    public ThreadLocal() {
     
    }

    /**
     * 获取当前线程的副本变量值
     * 步骤:
     * 1.获取当前线程的ThreadLocalMap对象threadLocals
     * 2.从map中获取线程存储的K-V Entry节点。
     * 3.从Entry节点获取存储的Value副本值返回。
     * 4.map为空的话返回初始值null,即线程变量副本为null,在使用时需要注意判断NullPointerException。
     */
    public T get() {
     
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
     
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
     
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

    /**
     * 设置当前线程初始化副本变量值
     *
     * @return the initial value
     */
    private T setInitialValue() {
     
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

    /**
     * 修改当前线程副本变量,设置一个指定的值
     */
    public void set(T value) {
     
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    /**
     * Removes the current thread's value for this thread-local
     * variable.  If this thread-local variable is subsequently
     * {@linkplain #get read} by the current thread, its value will be
     * reinitialized by invoking its {@link #initialValue} method,
     * unless its value is {@linkplain #set set} by the current thread
     * in the interim.  This may result in multiple invocations of the
     * {@code initialValue} method in the current thread.
     *
     * @since 1.5
     */
     public void remove() {
     
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

    /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
     
        return t.threadLocals;
    }

    /**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     */
    void createMap(Thread t, T firstValue) {
     
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

    /**
     * Factory method to create map of inherited thread locals.
     * Designed to be called only from Thread constructor.
     *
     * @param  parentMap the map associated with parent thread
     * @return a map containing the parent's inheritable bindings
     */
    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
     
        return new ThreadLocalMap(parentMap);
    }

    /**
     * Method childValue is visibly defined in subclass
     * InheritableThreadLocal, but is internally defined here for the
     * sake of providing createInheritedMap factory method without
     * needing to subclass the map class in InheritableThreadLocal.
     * This technique is preferable to the alternative of embedding
     * instanceof tests in methods.
     */
    T childValue(T parentValue) {
     
        throw new UnsupportedOperationException();
    }

    /**
     * An extension of ThreadLocal that obtains its initial value from
     * the specified {@code Supplier}.
     */
    static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
     

        private final Supplier<? extends T> supplier;

        SuppliedThreadLocal(Supplier<? extends T> supplier) {
     
            this.supplier = Objects.requireNonNull(supplier);
        }

        @Override
        protected T initialValue() {
     
            return supplier.get();
        }
    }

    /**
     * ThreadLocalMap is a customized hash map suitable only for
     * maintaining thread local values. No operations are exported
     * outside of the ThreadLocal class. The class is package private to
     * allow declaration of fields in class Thread.  To help deal with
     * very large and long-lived usages, the hash table entries use
     * WeakReferences for keys. However, since reference queues are not
     * used, stale entries are guaranteed to be removed only when
     * the table starts running out of space.
     */
    static class ThreadLocalMap {
     

        /**
         * 继承了WeakReference弱引用
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
     
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
     
                super(k);
                value = v;
            }
        }

        /**
         * The initial capacity -- MUST be a power of two.
         */
        private static final int INITIAL_CAPACITY = 16;

        /**
         * The table, resized as necessary.
         * table.length MUST always be a power of two.
         */
        private Entry[] table;

        /**
         * The number of entries in the table.
         */
        private int size = 0;

        /**
         * The next size value at which to resize.
         */
        private int threshold; // Default to 0

        /**
         * Set the resize threshold to maintain at worst a 2/3 load factor.
         */
        private void setThreshold(int len) {
     
            threshold = len * 2 / 3;
        }

        /**
         * Increment i modulo len.
         */
        private static int nextIndex(int i, int len) {
     
            return ((i + 1 < len) ? i + 1 : 0);
        }

        /**
         * Decrement i modulo len.
         */
        private static int prevIndex(int i, int len) {
     
            return ((i - 1 >= 0) ? i - 1 : len - 1);
        }

        /**
         * Construct a new map initially containing (firstKey, firstValue).
         * ThreadLocalMaps are constructed lazily, so we only create
         * one when we have at least one entry to put in it.
         */
        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
     
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

        /**
         * Construct a new map including all Inheritable ThreadLocals
         * from given parent map. Called only by createInheritedMap.
         *
         * @param parentMap the map associated with parent thread.
         */
        private ThreadLocalMap(ThreadLocalMap parentMap) {
     
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
     
                Entry e = parentTable[j];
                if (e != null) {
     
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
     
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

        /**
         * Get the entry associated with key.  This method
         * itself handles only the fast path: a direct hit of existing
         * key. It otherwise relays to getEntryAfterMiss.  This is
         * designed to maximize performance for direct hits, in part
         * by making this method readily inlinable.
         *
         * @param  key the thread local object
         * @return the entry associated with key, or null if no such
         */
        private Entry getEntry(ThreadLocal<?> key) {
     
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }

        /**
         * Version of getEntry method for use when key is not found in
         * its direct hash slot.
         *
         * @param  key the thread local object
         * @param  i the table index for key's hash code
         * @param  e the entry at table[i]
         * @return the entry associated with key, or null if no such
         */
        private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
     
            Entry[] tab = table;
            int len = tab.length;

            while (e != null) {
     
                ThreadLocal<?> k = e.get();
                if (k == key)
                    return e;
                if (k == null)
                    expungeStaleEntry(i);
                else
                    i = nextIndex(i, len);
                e = tab[i];
            }
            return null;
        }

        /**
         * Set the value associated with key.
         *
         * @param key the thread local object
         * @param value the value to be set
         */
        private void set(ThreadLocal<?> key, Object value) {
     

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
     
                ThreadLocal<?> k = e.get();

                if (k == key) {
     
                    e.value = value;
                    return;
                }

                if (k == null) {
     
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

        /**
         * Remove the entry for key.
         */
        private void remove(ThreadLocal<?> key) {
     
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
     
                if (e.get() == key) {
     
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }

        /**
         * Replace a stale entry encountered during a set operation
         * with an entry for the specified key.  The value passed in
         * the value parameter is stored in the entry, whether or not
         * an entry already exists for the specified key.
         *
         * As a side effect, this method expunges all stale entries in the
         * "run" containing the stale entry.  (A run is a sequence of entries
         * between two null slots.)
         *
         * @param  key the key
         * @param  value the value to be associated with key
         * @param  staleSlot index of the first stale entry encountered while
         *         searching for key.
         */
        private void replaceStaleEntry(ThreadLocal<?> key, Object value,
                                       int staleSlot) {
     
            Entry[] tab = table;
            int len = tab.length;
            Entry e;

            // Back up to check for prior stale entry in current run.
            // We clean out whole runs at a time to avoid continual
            // incremental rehashing due to garbage collector freeing
            // up refs in bunches (i.e., whenever the collector runs).
            int slotToExpunge = staleSlot;
            for (int i = prevIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = prevIndex(i, len))
                if (e.get() == null)
                    slotToExpunge = i;

            // Find either the key or trailing null slot of run, whichever
            // occurs first
            for (int i = nextIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
     
                ThreadLocal<?> k = e.get();

                // If we find key, then we need to swap it
                // with the stale entry to maintain hash table order.
                // The newly stale slot, or any other stale slot
                // encountered above it, can then be sent to expungeStaleEntry
                // to remove or rehash all of the other entries in run.
                if (k == key) {
     
                    e.value = value;

                    tab[i] = tab[staleSlot];
                    tab[staleSlot] = e;

                    // Start expunge at preceding stale entry if it exists
                    if (slotToExpunge == staleSlot)
                        slotToExpunge = i;
                    cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
                    return;
                }

                // If we didn't find stale entry on backward scan, the
                // first stale entry seen while scanning for key is the
                // first still present in the run.
                if (k == null && slotToExpunge == staleSlot)
                    slotToExpunge = i;
            }

            // If key not found, put new entry in stale slot
            tab[staleSlot].value = null;
            tab[staleSlot] = new Entry(key, value);

            // If there are any other stale entries in run, expunge them
            if (slotToExpunge != staleSlot)
                cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
        }

        /**
         * Expunge a stale entry by rehashing any possibly colliding entries
         * lying between staleSlot and the next null slot.  This also expunges
         * any other stale entries encountered before the trailing null.  See
         * Knuth, Section 6.4
         *
         * @param staleSlot index of slot known to have null key
         * @return the index of the next null slot after staleSlot
         * (all between staleSlot and this slot will have been checked
         * for expunging).
         */
        private int expungeStaleEntry(int staleSlot) {
     
            Entry[] tab = table;
            int len = tab.length;

            // expunge entry at staleSlot
            tab[staleSlot].value = null;
            tab[staleSlot] = null;
            size--;

            // Rehash until we encounter null
            Entry e;
            int i;
            for (i = nextIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
     
                ThreadLocal<?> k = e.get();
                if (k == null) {
     
                    e.value = null;
                    tab[i] = null;
                    size--;
                } else {
     
                    int h = k.threadLocalHashCode & (len - 1);
                    if (h != i) {
     
                        tab[i] = null;

                        // Unlike Knuth 6.4 Algorithm R, we must scan until
                        // null because multiple entries could have been stale.
                        while (tab[h] != null)
                            h = nextIndex(h, len);
                        tab[h] = e;
                    }
                }
            }
            return i;
        }

        /**
         * Heuristically scan some cells looking for stale entries.
         * This is invoked when either a new element is added, or
         * another stale one has been expunged. It performs a
         * logarithmic number of scans, as a balance between no
         * scanning (fast but retains garbage) and a number of scans
         * proportional to number of elements, that would find all
         * garbage but would cause some insertions to take O(n) time.
         *
         * @param i a position known NOT to hold a stale entry. The
         * scan starts at the element after i.
         *
         * @param n scan control: {@code log2(n)} cells are scanned,
         * unless a stale entry is found, in which case
         * {@code log2(table.length)-1} additional cells are scanned.
         * When called from insertions, this parameter is the number
         * of elements, but when from replaceStaleEntry, it is the
         * table length. (Note: all this could be changed to be either
         * more or less aggressive by weighting n instead of just
         * using straight log n. But this version is simple, fast, and
         * seems to work well.)
         *
         * @return true if any stale entries have been removed.
         */
        private boolean cleanSomeSlots(int i, int n) {
     
            boolean removed = false;
            Entry[] tab = table;
            int len = tab.length;
            do {
     
                i = nextIndex(i, len);
                Entry e = tab[i];
                if (e != null && e.get() == null) {
     
                    n = len;
                    removed = true;
                    i = expungeStaleEntry(i);
                }
            } while ( (n >>>= 1) != 0);
            return removed;
        }

        /**
         * Re-pack and/or re-size the table. First scan the entire
         * table removing stale entries. If this doesn't sufficiently
         * shrink the size of the table, double the table size.
         */
        private void rehash() {
     
            expungeStaleEntries();

            // Use lower threshold for doubling to avoid hysteresis
            if (size >= threshold - threshold / 4)
                resize();
        }

        /**
         * Double the capacity of the table.
         */
        private void resize() {
     
            Entry[] oldTab = table;
            int oldLen = oldTab.length;
            int newLen = oldLen * 2;
            Entry[] newTab = new Entry[newLen];
            int count = 0;

            for (int j = 0; j < oldLen; ++j) {
     
                Entry e = oldTab[j];
                if (e != null) {
     
                    ThreadLocal<?> k = e.get();
                    if (k == null) {
     
                        e.value = null; // Help the GC
                    } else {
     
                        int h = k.threadLocalHashCode & (newLen - 1);
                        while (newTab[h] != null)
                            h = nextIndex(h, newLen);
                        newTab[h] = e;
                        count++;
                    }
                }
            }

            setThreshold(newLen);
            size = count;
            table = newTab;
        }

        /**
         * Expunge all stale entries in the table.
         */
        private void expungeStaleEntries() {
     
            Entry[] tab = table;
            int len = tab.length;
            for (int j = 0; j < len; j++) {
     
                Entry e = tab[j];
                if (e != null && e.get() == null)
                    expungeStaleEntry(j);
            }
        }
    }
}

Thread类源码

/**
 * 线程类
 */
public
class Thread implements Runnable {
     
    private static native void registerNatives();
    static {
     
        registerNatives();
    }

    private volatile String name;
    private int            priority;
    private Thread         threadQ;
    private long           eetop;

    /* Whether or not to single_step this thread. */
    private boolean     single_step;

    /* Whether or not the thread is a daemon thread. */
    private boolean     daemon = false;

    /* JVM state */
    private boolean     stillborn = false;

    /* What will be run. */
    private Runnable target;

    /* The group of this thread */
    private ThreadGroup group;

    /* The context ClassLoader for this thread */
    private ClassLoader contextClassLoader;

    /* The inherited AccessControlContext of this thread */
    private AccessControlContext inheritedAccessControlContext;

    /* For autonumbering anonymous threads. */
    private static int threadInitNumber;
    private static synchronized int nextThreadNum() {
     
        return threadInitNumber++;
    }

    /* ThreadLocal类存放线程变量副本*/
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

    /*
     * The requested stack size for this thread, or 0 if the creator did
     * not specify a stack size.  It is up to the VM to do whatever it
     * likes with this number; some VMs will ignore it.
     */
    private long stackSize;

    /*
     * JVM-private state that persists after native thread termination.
     */
    private long nativeParkEventPointer;

    /*
     * Thread ID
     */
    private long tid;

    /* For generating thread ID */
    private static long threadSeqNumber;

    /* 
    * 线程状态 volatile
     */
    private volatile int threadStatus = 0;


    private static synchronized long nextThreadID() {
     
        return ++threadSeqNumber;
    }

    /**
     * The argument supplied to the current call to
     * java.util.concurrent.locks.LockSupport.park.
     * Set by (private) java.util.concurrent.locks.LockSupport.setBlocker
     * Accessed using java.util.concurrent.locks.LockSupport.getBlocker
     */
    volatile Object parkBlocker;

    /* The object in which this thread is blocked in an interruptible I/O
     * operation, if any.  The blocker's interrupt method should be invoked
     * after setting this thread's interrupt status.
     */
    private volatile Interruptible blocker;
    private final Object blockerLock = new Object();

    /* Set the blocker field; invoked via sun.misc.SharedSecrets from java.nio code
     */
    void blockedOn(Interruptible b) {
     
        synchronized (blockerLock) {
     
            blocker = b;
        }
    }

    /**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;

    /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;

    /**
     *返回当前线程
     */
    public static native Thread currentThread();

    /**
     * A hint to the scheduler that the current thread is willing to yield
     * its current use of a processor. The scheduler is free to ignore this
     * hint.
     */
    public static native void yield();

    /**
     *暂停n毫秒
     */
    public static native void sleep(long millis) throws InterruptedException;

    /**
     * 暂停milliseconds毫秒 + nanoseconds 纳秒
     */
    public static void sleep(long millis, int nanos)
            throws InterruptedException {
     
        if (millis < 0) {
     
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
     
            throw new IllegalArgumentException(
                    "nanosecond timeout value out of range");
        }

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
     
            millis++;
        }

        sleep(millis);
    }

    /**
     * Initializes a Thread with the current AccessControlContext.
     * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext,boolean)
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
     
        init(g, target, name, stackSize, null, true);
    }

    /**
     * 初始化一个线程
     *
     * @param g the Thread group
     * @param target the object whose run() method gets called
     * @param name the name of the new Thread
     * @param stackSize the desired stack size for the new thread, or
     *        zero to indicate that this parameter is to be ignored.
     * @param acc the AccessControlContext to inherit, or
     *            AccessController.getContext() if null
     * @param inheritThreadLocals if {@code true}, inherit initial values for
     *            inheritable thread-locals from the constructing thread
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
     
        if (name == null) {
     
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;

        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
     
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
     
                g = security.getThreadGroup();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
     
                g = parent.getThreadGroup();
            }
        }

        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();

        /*
         * Do we have the required permissions?
         */
        if (security != null) {
     
            if (isCCLOverridden(getClass())) {
     
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                    ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

    /**
     * 线程不能克隆,报异常
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
     
        throw new CloneNotSupportedException();
    }

    /**
     * 无参构造方法
     */
    public Thread() {
     
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }

    /**
     * 实现Runnable 接口创建线程
     */
    public Thread(Runnable target) {
     
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

    /**
     * Creates a new Thread that inherits the given AccessControlContext.
     * This is not a public constructor.
     */
    Thread(Runnable target, AccessControlContext acc) {
     
        init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
    }

    /**
     * 实现Runnable 接口创建线程,指定线程组
     */
    public Thread(ThreadGroup group, Runnable target) {
     
        init(group, target, "Thread-" + nextThreadNum(), 0);
    }

    /**
     * 创建一个自定义名称的线程
     */
    public Thread(String name) {
     
        init(null, null, name, 0);
    }

    /**
     * 创建一个自定义名称的线程,指定线程组
     */
    public Thread(ThreadGroup group, String name) {
     
        init(group, null, name, 0);
    }

    /**
     * 实现Runnable 接口创建线程,指定线程名称
     */
    public Thread(Runnable target, String name) {
     
        init(null, target, name, 0);
    }

    /**
     * 实现Runnable 接口创建线程,指定线程名称
     */
    public Thread(ThreadGroup group, Runnable target, String name) {
     
        init(group, target, name, 0);
    }

    /**
     * Allocates a new {@code Thread} object so that it has {@code target}
     * as its run object, has the specified {@code name} as its name,
     * and belongs to the thread group referred to by {@code group}, and has
     * the specified stack size.
     *
     * @param  group
     *         the thread group. If {@code null} and there is a security
     *         manager, the group is determined by {@linkplain
     *         SecurityManager#getThreadGroup SecurityManager.getThreadGroup()}.
     *         If there is not a security manager or {@code
     *         SecurityManager.getThreadGroup()} returns {@code null}, the group
     *         is set to the current thread's thread group.
     *
     * @param  target
     *         the object whose {@code run} method is invoked when this thread
     *         is started. If {@code null}, this thread's run method is invoked.
     *
     * @param  name
     *         the name of the new thread
     *
     * @param  stackSize
     *         the desired stack size for the new thread, or zero to indicate
     *         that this parameter is to be ignored.
     *
     * @throws  SecurityException
     *          if the current thread cannot create a thread in the specified
     *          thread group
     *
     * @since 1.4
     */
    public Thread(ThreadGroup group, Runnable target, String name,
                  long stackSize) {
     
        init(group, target, name, stackSize);
    }

    /**
     * Causes this thread to begin execution; the Java Virtual Machine
     * calls the run method of this thread.
     * 

* The result is that two threads are running concurrently: the * current thread (which returns from the call to the * start method) and the other thread (which executes its * run method). *

* It is never legal to start a thread more than once. * In particular, a thread may not be restarted once it has completed * execution. * * @exception IllegalThreadStateException if the thread was already * started. * @see #run() * @see #stop() */ public synchronized void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ if (threadStatus != 0) throw new IllegalThreadStateException(); /* Notify the group that this thread is about to be started * so that it can be added to the group's list of threads * and the group's unstarted count can be decremented. */ group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } } private native void start0(); /** * If this thread was constructed using a separate * Runnable run object, then that * Runnable object's run method is called; * otherwise, this method does nothing and returns. *

* Subclasses of Thread should override this method. * * @see #start() * @see #stop() * @see #Thread(ThreadGroup, Runnable, String) */ @Override public void run() { if (target != null) { target.run(); } } /** * This method is called by the system to give a Thread * a chance to clean up before it actually exits. */ private void exit() { if (group != null) { group.threadTerminated(this); group = null; } /* Aggressively null out all reference fields: see bug 4006245 */ target = null; /* Speed the release of some of these resources */ threadLocals = null; inheritableThreadLocals = null; inheritedAccessControlContext = null; blocker = null; uncaughtExceptionHandler = null; } /** * Forces the thread to stop executing. *

* If there is a security manager installed, its checkAccess * method is called with this * as its argument. This may result in a * * @exception SecurityException if the current thread cannot * modify this thread. * @see #interrupt() * @see #checkAccess() * @see #run() * @see #start() * @see ThreadDeath * @see ThreadGroup#uncaughtException(Thread,Throwable) * @see SecurityManager#checkAccess(Thread) * @see SecurityManager#checkPermission */ @Deprecated public final void stop() { SecurityManager security = System.getSecurityManager(); if (security != null) { checkAccess(); if (this != Thread.currentThread()) { security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION); } } // A zero status value corresponds to "NEW", it can't change to // not-NEW because we hold the lock. if (threadStatus != 0) { resume(); // Wake up thread if it was suspended; no-op otherwise } // The VM can handle all thread states stop0(new ThreadDeath()); } /** * Throws {@code UnsupportedOperationException}. * * @param obj ignored * * @deprecated This method was originally designed to force a thread to stop * and throw a given {@code Throwable} as an exception. It was * inherently unsafe (see {@link #stop()} for details), and furthermore * could be used to generate exceptions that the target thread was * not prepared to handle. * For more information, see * Why * are Thread.stop, Thread.suspend and Thread.resume Deprecated?. */ @Deprecated public final synchronized void stop(Throwable obj) { throw new UnsupportedOperationException(); } /** * Interrupts this thread. * *

Unless the current thread is interrupting itself, which is * always permitted, the {@link #checkAccess() checkAccess} method * of this thread is invoked, which may cause a {@link * SecurityException} to be thrown. * */ public void interrupt() { if (this != Thread.currentThread()) checkAccess(); synchronized (blockerLock) { Interruptible b = blocker; if (b != null) { interrupt0(); // Just to set the interrupt flag b.interrupt(this); return; } } interrupt0(); } /** * Tests whether the current thread has been interrupted. The * interrupted status of the thread is cleared by this method. In * other words, if this method were to be called twice in succession, the * second call would return false (unless the current thread were * interrupted again, after the first call had cleared its interrupted * status and before the second call had examined it). * *

A thread interruption ignored because a thread was not alive * at the time of the interrupt will be reflected by this method * returning false. * * @return true if the current thread has been interrupted; * false otherwise. * @see #isInterrupted() * @revised 6.0 */ public static boolean interrupted() { return currentThread().isInterrupted(true); } /** * Tests whether this thread has been interrupted. The interrupted * status of the thread is unaffected by this method. * *

A thread interruption ignored because a thread was not alive * at the time of the interrupt will be reflected by this method * returning false. * * @return true if this thread has been interrupted; * false otherwise. * @see #interrupted() * @revised 6.0 */ public boolean isInterrupted() { return isInterrupted(false); } /** * Tests if some Thread has been interrupted. The interrupted state * is reset or not based on the value of ClearInterrupted that is * passed. */ private native boolean isInterrupted(boolean ClearInterrupted); /** * Throws {@link NoSuchMethodError}. * * @deprecated This method was originally designed to destroy this * thread without any cleanup. Any monitors it held would have * remained locked. However, the method was never implemented. * If if were to be implemented, it would be deadlock-prone in * much the manner of {@link #suspend}. If the target thread held * a lock protecting a critical system resource when it was * destroyed, no thread could ever access this resource again. * If another thread ever attempted to lock this resource, deadlock * would result. Such deadlocks typically manifest themselves as * "frozen" processes. For more information, see * * Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?. * @throws NoSuchMethodError always */ @Deprecated public void destroy() { throw new NoSuchMethodError(); } /** * Tests if this thread is alive. A thread is alive if it has * been started and has not yet died. * * @return true if this thread is alive; * false otherwise. */ public final native boolean isAlive(); /** * Suspends this thread. *

* First, the checkAccess method of this thread is called * with no arguments. This may result in throwing a * SecurityException (in the current thread). *

* If the thread is alive, it is suspended and makes no further * progress unless and until it is resumed. * * @exception SecurityException if the current thread cannot modify * this thread. * @see #checkAccess * @deprecated This method has been deprecated, as it is * inherently deadlock-prone. If the target thread holds a lock on the * monitor protecting a critical system resource when it is suspended, no * thread can access this resource until the target thread is resumed. If * the thread that would resume the target thread attempts to lock this * monitor prior to calling resume, deadlock results. Such * deadlocks typically manifest themselves as "frozen" processes. * For more information, see * Why * are Thread.stop, Thread.suspend and Thread.resume Deprecated?. */ @Deprecated public final void suspend() { checkAccess(); suspend0(); } /** * Resumes a suspended thread. *

* First, the checkAccess method of this thread is called * with no arguments. This may result in throwing a * SecurityException (in the current thread). *

* If the thread is alive but suspended, it is resumed and is * permitted to make progress in its execution. * * @exception SecurityException if the current thread cannot modify this * thread. * @see #checkAccess * @see #suspend() * @deprecated This method exists solely for use with {@link #suspend}, * which has been deprecated because it is deadlock-prone. * For more information, see * Why * are Thread.stop, Thread.suspend and Thread.resume Deprecated?. */ @Deprecated public final void resume() { checkAccess(); resume0(); } /** * Changes the priority of this thread. *

* First the checkAccess method of this thread is called * with no arguments. This may result in throwing a * SecurityException. *

* Otherwise, the priority of this thread is set to the smaller of * the specified newPriority and the maximum permitted * priority of the thread's thread group. * * @param newPriority priority to set this thread to * @exception IllegalArgumentException If the priority is not in the * range MIN_PRIORITY to * MAX_PRIORITY. * @exception SecurityException if the current thread cannot modify * this thread. * @see #getPriority * @see #checkAccess() * @see #getThreadGroup() * @see #MAX_PRIORITY * @see #MIN_PRIORITY * @see ThreadGroup#getMaxPriority() */ public final void setPriority(int newPriority) { ThreadGroup g; checkAccess(); if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) { throw new IllegalArgumentException(); } if((g = getThreadGroup()) != null) { if (newPriority > g.getMaxPriority()) { newPriority = g.getMaxPriority(); } setPriority0(priority = newPriority); } } /** * Returns this thread's priority. * * @return this thread's priority. * @see #setPriority */ public final int getPriority() { return priority; } /** * Changes the name of this thread to be equal to the argument * name. *

* First the checkAccess method of this thread is called * with no arguments. This may result in throwing a * SecurityException. * * @param name the new name for this thread. * @exception SecurityException if the current thread cannot modify this * thread. * @see #getName * @see #checkAccess() */ public final synchronized void setName(String name) { checkAccess(); if (name == null) { throw new NullPointerException("name cannot be null"); } this.name = name; if (threadStatus != 0) { setNativeName(name); } } /** * Returns this thread's name. * * @return this thread's name. * @see #setName(String) */ public final String getName() { return name; } /** * Returns the thread group to which this thread belongs. * This method returns null if this thread has died * (been stopped). * * @return this thread's thread group. */ public final ThreadGroup getThreadGroup() { return group; } /** * Returns an estimate of the number of active threads in the current * thread's {@linkplain java.lang.ThreadGroup thread group} and its * subgroups. Recursively iterates over all subgroups in the current * thread's thread group. * *

The value returned is only an estimate because the number of * threads may change dynamically while this method traverses internal * data structures, and might be affected by the presence of certain * system threads. This method is intended primarily for debugging * and monitoring purposes. * * @return an estimate of the number of active threads in the current * thread's thread group and in any other thread group that * has the current thread's thread group as an ancestor */ public static int activeCount() { return currentThread().getThreadGroup().activeCount(); } /** * Copies into the specified array every active thread in the current * thread's thread group and its subgroups. This method simply * invokes the {@link java.lang.ThreadGroup#enumerate(Thread[])} * method of the current thread's thread group. * * @param tarray * an array into which to put the list of threads * * @return the number of threads put into the array */ public static int enumerate(Thread tarray[]) { return currentThread().getThreadGroup().enumerate(tarray); } /** * Counts the number of stack frames in this thread. The thread must * be suspended. * * @return the number of stack frames in this thread. * @exception IllegalThreadStateException if this thread is not * suspended. * @deprecated The definition of this call depends on {@link #suspend}, * which is deprecated. Further, the results of this call * were never well-defined. */ @Deprecated public native int countStackFrames(); /** * Waits at most {@code millis} milliseconds for this thread to * die. A timeout of {@code 0} means to wait forever * * @param millis * the time to wait in milliseconds */ public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } } /** * Waits at most {@code millis} milliseconds plus * {@code nanos} nanoseconds for this thread to die. * @param millis * the time to wait in milliseconds * * @param nanos * {@code 0-999999} additional nanoseconds to wait */ public final synchronized void join(long millis, int nanos) throws InterruptedException { if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos >= 500000 || (nanos != 0 && millis == 0)) { millis++; } join(millis); } /** * Waits for this thread to die. * *

An invocation of this method behaves in exactly the same * way as the invocation */ public final void join() throws InterruptedException { join(0); } /** * Prints a stack trace of the current thread to the standard error stream. * This method is used only for debugging. * * @see Throwable#printStackTrace() */ public static void dumpStack() { new Exception("Stack trace").printStackTrace(); } /** * Marks this thread as either a {@linkplain #isDaemon daemon} thread * or a user thread. The Java Virtual Machine exits when the only * threads running are all daemon threads. * *

This method must be invoked before the thread is started. * * @param on * if {@code true}, marks this thread as a daemon thread */ public final void setDaemon(boolean on) { checkAccess(); if (isAlive()) { throw new IllegalThreadStateException(); } daemon = on; } /** * Tests if this thread is a daemon thread. * * @return true if this thread is a daemon thread; * false otherwise. * @see #setDaemon(boolean) */ public final boolean isDaemon() { return daemon; } /** * Determines if the currently running thread has permission to * modify this thread. *

* If there is a security manager, its checkAccess method * is called with this thread as its argument. This may result in * throwing a SecurityException. * * @exception SecurityException if the current thread is not allowed to * access this thread. * @see SecurityManager#checkAccess(Thread) */ public final void checkAccess() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkAccess(this); } } /** * Returns a string representation of this thread, including the * thread's name, priority, and thread group. * * @return a string representation of this thread. */ public String toString() { ThreadGroup group = getThreadGroup(); if (group != null) { return "Thread[" + getName() + "," + getPriority() + "," + group.getName() + "]"; } else { return "Thread[" + getName() + "," + getPriority() + "," + "" + "]"; } } /** * Returns the context ClassLoader for this Thread. The context * ClassLoader is provided by the creator of the thread for use * by code running in this thread when loading classes and resources. * If not {@linkplain #setContextClassLoader set}, the default is the * ClassLoader context of the parent Thread. The context ClassLoader of the * primordial thread is typically set to the class loader used to load the * application. * * @return the context ClassLoader for this Thread, or {@code null} * indicating the system class loader (or, failing that, the * bootstrap class loader) */ @CallerSensitive public ClassLoader getContextClassLoader() { if (contextClassLoader == null) return null; SecurityManager sm = System.getSecurityManager(); if (sm != null) { ClassLoader.checkClassLoaderPermission(contextClassLoader, Reflection.getCallerClass()); } return contextClassLoader; } /** * Sets the context ClassLoader for this Thread. The context * ClassLoader can be set when a thread is created, and allows * the creator of the thread to provide the appropriate class loader, * through {@code getContextClassLoader}, to code running in the thread * when loading classes and resources. * * @param cl * the context ClassLoader for this Thread, or null indicating the * system class loader (or, failing that, the bootstrap class loader) */ public void setContextClassLoader(ClassLoader cl) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission("setContextClassLoader")); } contextClassLoader = cl; } /** * Returns true if and only if the current thread holds the * monitor lock on the specified object. * *

This method is designed to allow a program to assert that * the current thread already holds a specified lock: *

     *     assert Thread.holdsLock(obj);
     * 
* * @param obj the object on which to test lock ownership * @throws NullPointerException if obj is null * @return true if the current thread holds the monitor lock on * the specified object. * @since 1.4 */
public static native boolean holdsLock(Object obj); private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0]; /** * Returns an array of stack trace elements representing the stack dump * of this thread. This method will return a zero-length array if * this thread has not started, has started but has not yet been * scheduled to run by the system, or has terminated. * If the returned array is of non-zero length then the first element of * the array represents the top of the stack, which is the most recent * method invocation in the sequence. The last element of the array * represents the bottom of the stack, which is the least recent method * invocation in the sequence. * *

If there is a security manager, and this thread is not * the current thread, then the security manager's * checkPermission method is called with a * RuntimePermission("getStackTrace") permission * to see if it's ok to get the stack trace. * *

Some virtual machines may, under some circumstances, omit one * or more stack frames from the stack trace. In the extreme case, * a virtual machine that has no stack trace information concerning * this thread is permitted to return a zero-length array from this * method. * * @return an array of StackTraceElement, * each represents one stack frame. * */ public StackTraceElement[] getStackTrace() { if (this != Thread.currentThread()) { // check for getStackTrace permission SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission( SecurityConstants.GET_STACK_TRACE_PERMISSION); } // optimization so we do not call into the vm for threads that // have not yet started or have terminated if (!isAlive()) { return EMPTY_STACK_TRACE; } StackTraceElement[][] stackTraceArray = dumpThreads(new Thread[] { this}); StackTraceElement[] stackTrace = stackTraceArray[0]; // a thread that was alive during the previous isAlive call may have // since terminated, therefore not having a stacktrace. if (stackTrace == null) { stackTrace = EMPTY_STACK_TRACE; } return stackTrace; } else { // Don't need JVM help for current thread return (new Exception()).getStackTrace(); } } /** * Returns a map of stack traces for all live threads. * The map keys are threads and each map value is an array of * StackTraceElement that represents the stack dump * of the corresponding Thread. * The returned stack traces are in the format specified for * the {@link #getStackTrace getStackTrace} method. * * @return a Map from Thread to an array of * StackTraceElement that represents the stack trace of * the corresponding thread. */ public static Map<Thread, StackTraceElement[]> getAllStackTraces() { // check for getStackTrace permission SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission( SecurityConstants.GET_STACK_TRACE_PERMISSION); security.checkPermission( SecurityConstants.MODIFY_THREADGROUP_PERMISSION); } // Get a snapshot of the list of all threads Thread[] threads = getThreads(); StackTraceElement[][] traces = dumpThreads(threads); Map<Thread, StackTraceElement[]> m = new HashMap<>(threads.length); for (int i = 0; i < threads.length; i++) { StackTraceElement[] stackTrace = traces[i]; if (stackTrace != null) { m.put(threads[i], stackTrace); } // else terminated so we don't put it in the map } return m; } private static final RuntimePermission SUBCLASS_IMPLEMENTATION_PERMISSION = new RuntimePermission("enableContextClassLoaderOverride"); /** cache of subclass security audit results */ /* Replace with ConcurrentReferenceHashMap when/if it appears in a future * release */ private static class Caches { /** cache of subclass security audit results */ static final ConcurrentMap<WeakClassKey,Boolean> subclassAudits = new ConcurrentHashMap<>(); /** queue for WeakReferences to audited subclasses */ static final ReferenceQueue<Class<?>> subclassAuditsQueue = new ReferenceQueue<>(); } /** * Verifies that this (possibly subclass) instance can be constructed * without violating security constraints: the subclass must not override * security-sensitive non-final methods, or else the * "enableContextClassLoaderOverride" RuntimePermission is checked. */ private static boolean isCCLOverridden(Class<?> cl) { if (cl == Thread.class) return false; processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits); WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue); Boolean result = Caches.subclassAudits.get(key); if (result == null) { result = Boolean.valueOf(auditSubclass(cl)); Caches.subclassAudits.putIfAbsent(key, result); } return result.booleanValue(); } /** * Performs reflective checks on given subclass to verify that it doesn't * override security-sensitive non-final methods. Returns true if the * subclass overrides any of the methods, false otherwise. */ private static boolean auditSubclass(final Class<?> subcl) { Boolean result = AccessController.doPrivileged( new PrivilegedAction<Boolean>() { public Boolean run() { for (Class<?> cl = subcl; cl != Thread.class; cl = cl.getSuperclass()) { try { cl.getDeclaredMethod("getContextClassLoader", new Class<?>[0]); return Boolean.TRUE; } catch (NoSuchMethodException ex) { } try { Class<?>[] params = { ClassLoader.class}; cl.getDeclaredMethod("setContextClassLoader", params); return Boolean.TRUE; } catch (NoSuchMethodException ex) { } } return Boolean.FALSE; } } ); return result.booleanValue(); } private native static StackTraceElement[][] dumpThreads(Thread[] threads); private native static Thread[] getThreads(); /** * Returns the identifier of this Thread. The thread ID is a positive * long number generated when this thread was created. * The thread ID is unique and remains unchanged during its lifetime. * When a thread is terminated, this thread ID may be reused. * * @return this thread's ID. * @since 1.5 */ public long getId() { return tid; } /** * A thread state. A thread can be in one of the following states: */ public enum State { /** * Thread state for a thread which has not yet started. */ NEW, /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */ RUNNABLE, /** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. */ BLOCKED, /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: *

    *
  • {@link Object#wait() Object.wait} with no timeout
  • *
  • {@link #join() Thread.join} with no timeout
  • *
  • {@link LockSupport#park() LockSupport.park}
  • *
* *

A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called Object.wait() * on an object is waiting for another thread to call * Object.notify() or Object.notifyAll() on * that object. A thread that has called Thread.join() * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: *

    *
  • {@link #sleep Thread.sleep}
  • *
  • {@link Object#wait(long) Object.wait} with timeout
  • *
  • {@link #join(long) Thread.join} with timeout
  • *
  • {@link LockSupport#parkNanos LockSupport.parkNanos}
  • *
  • {@link LockSupport#parkUntil LockSupport.parkUntil}
  • *
*/
TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; } /** * Returns the state of this thread. * This method is designed for use in monitoring of the system state, * not for synchronization control. * * @return this thread's state. * @since 1.5 */ public State getState() { // get current thread state return sun.misc.VM.toThreadState(threadStatus); } // Added in JSR-166 /** * Interface for handlers invoked when a Thread abruptly * terminates due to an uncaught exception. *

When a thread is about to terminate due to an uncaught exception * the Java Virtual Machine will query the thread for its * UncaughtExceptionHandler using * {@link #getUncaughtExceptionHandler} and will invoke the handler's * uncaughtException method, passing the thread and the * exception as arguments. * If a thread has not had its UncaughtExceptionHandler * explicitly set, then its ThreadGroup object acts as its * UncaughtExceptionHandler. If the ThreadGroup object * has no * special requirements for dealing with the exception, it can forward * the invocation to the {@linkplain #getDefaultUncaughtExceptionHandler * default uncaught exception handler}. * * @see #setDefaultUncaughtExceptionHandler * @see #setUncaughtExceptionHandler * @see ThreadGroup#uncaughtException * @since 1.5 */ @FunctionalInterface public interface UncaughtExceptionHandler { /** * Method invoked when the given thread terminates due to the * given uncaught exception. *

Any exception thrown by this method will be ignored by the * Java Virtual Machine. * @param t the thread * @param e the exception */ void uncaughtException(Thread t, Throwable e); } // null unless explicitly set private volatile UncaughtExceptionHandler uncaughtExceptionHandler; // null unless explicitly set private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler; /** * Set the default handler invoked when a thread abruptly terminates * due to an uncaught exception, and no other handler has been defined * for that thread. */ public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission( new RuntimePermission("setDefaultUncaughtExceptionHandler") ); } defaultUncaughtExceptionHandler = eh; } /** * Returns the default handler invoked when a thread abruptly terminates * due to an uncaught exception. If the returned value is null, * there is no default. * @since 1.5 * @see #setDefaultUncaughtExceptionHandler * @return the default uncaught exception handler for all threads */ public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(){ return defaultUncaughtExceptionHandler; } /** * Returns the handler invoked when this thread abruptly terminates * due to an uncaught exception. If this thread has not had an * uncaught exception handler explicitly set then this thread's * ThreadGroup object is returned, unless this thread * has terminated, in which case null is returned. * @since 1.5 * @return the uncaught exception handler for this thread */ public UncaughtExceptionHandler getUncaughtExceptionHandler() { return uncaughtExceptionHandler != null ? uncaughtExceptionHandler : group; } /** * Set the handler invoked when this thread abruptly terminates * due to an uncaught exception. *

A thread can take full control of how it responds to uncaught * exceptions by having its uncaught exception handler explicitly set. * If no such handler is set then the thread's ThreadGroup * object acts as its handler. * @param eh the object to use as this thread's uncaught exception * handler. If null then this thread has no explicit handler. * @throws SecurityException if the current thread is not allowed to * modify this thread. * @see #setDefaultUncaughtExceptionHandler * @see ThreadGroup#uncaughtException * @since 1.5 */ public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) { checkAccess(); uncaughtExceptionHandler = eh; } /** * Dispatch an uncaught exception to the handler. This method is * intended to be called only by the JVM. */ private void dispatchUncaughtException(Throwable e) { getUncaughtExceptionHandler().uncaughtException(this, e); } /** * Removes from the specified map any keys that have been enqueued * on the specified reference queue. */ static void processQueue(ReferenceQueue<Class<?>> queue, ConcurrentMap<? extends WeakReference<Class<?>>, ?> map) { Reference<? extends Class<?>> ref; while((ref = queue.poll()) != null) { map.remove(ref); } } /** * Weak key for Class objects. **/ static class WeakClassKey extends WeakReference<Class<?>> { /** * saved value of the referent's identity hash code, to maintain * a consistent hash code after the referent has been cleared */ private final int hash; /** * Create a new WeakClassKey to the given object, registered * with a queue. */ WeakClassKey(Class<?> cl, ReferenceQueue<Class<?>> refQueue) { super(cl, refQueue); hash = System.identityHashCode(cl); } /** * Returns the identity hash code of the original referent. */ @Override public int hashCode() { return hash; } /** * Returns true if the given object is this identical * WeakClassKey instance, or, if this object's referent has not * been cleared, if the given object is another WeakClassKey * instance with the identical non-null referent as this one. */ @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj instanceof WeakClassKey) { Object referent = get(); return (referent != null) && (referent == ((WeakClassKey) obj).get()); } else { return false; } } } // The following three initially uninitialized fields are exclusively // managed by class java.util.concurrent.ThreadLocalRandom. These // fields are used to build the high-performance PRNGs in the // concurrent code, and we can not risk accidental false sharing. // Hence, the fields are isolated with @Contended. /** The current seed for a ThreadLocalRandom */ @sun.misc.Contended("tlr") long threadLocalRandomSeed; /** Probe hash value; nonzero if threadLocalRandomSeed initialized */ @sun.misc.Contended("tlr") int threadLocalRandomProbe; /** Secondary seed isolated from public ThreadLocalRandom sequence */ @sun.misc.Contended("tlr") int threadLocalRandomSecondarySeed; /* Some private helper methods */ private native void setPriority0(int newPriority); private native void stop0(Object o); private native void suspend0(); private native void resume0(); private native void interrupt0(); private native void setNativeName(String name); }

你可能感兴趣的:(Java基础,技术面试,ThreadLocal,ThreadLocalMap,内部类)