美文网首页
ThreadLocal源代码--read the fucking

ThreadLocal源代码--read the fucking

作者: 王岩_shang | 来源:发表于2016-07-29 16:00 被阅读104次
    • 直接看源代码吧
    
    package java.lang;
    import java.lang.ref.Reference;
    import java.lang.ref.WeakReference;
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * Implements a thread-local storage, that is, a variable for which each thread
     * has its own value. All threads share the same {@code ThreadLocal} object,
     * but each sees a different value when accessing it, and changes made by one
     * thread do not affect the other threads. The implementation supports
     * {@code null} values.
     *
     * @see java.lang.Thread
     * @author Bob Lee
     */
    public class ThreadLocal<T> {
    
        /* Thanks to Josh Bloch and Doug Lea for code reviews and impl advice. */
    
        /**
         * Creates a new thread-local variable.
         */
        public ThreadLocal() {}
    
        /**
         * Returns the value of this variable for the current thread. If an entry
         * doesn't yet exist for this variable on this thread, this method will
         * create an entry, populating the value with the result of
         * {@link #initialValue()}.
         *
         * @return the current value of the variable for the calling thread.
         */
        @SuppressWarnings("unchecked")
        public T get() {
            // Optimized for the fast path.
            Thread currentThread = Thread.currentThread();
            Values values = values(currentThread);
            if (values != null) {
                Object[] table = values.table;
                int index = hash & values.mask;
                if (this.reference == table[index]) {
                    return (T) table[index + 1];
                }
            } else {
                values = initializeValues(currentThread);
            }
    
            return (T) values.getAfterMiss(this);
        }
    
        /**
         * Provides the initial value of this variable for the current thread.
         * The default implementation returns {@code null}.
         *
         * @return the initial value of the variable.
         */
        protected T initialValue() {
            return null;
        }
    
        /**
         * Sets the value of this variable for the current thread. If set to
         * {@code null}, the value will be set to null and the underlying entry will
         * still be present.
         *
         * @param value the new value of the variable for the caller thread.
         */
        public void set(T value) {
            Thread currentThread = Thread.currentThread();
            Values values = values(currentThread);
            if (values == null) {
                values = initializeValues(currentThread);
            }
            values.put(this, value);
        }
    
        /**
         * Removes the entry for this variable in the current thread. If this call
         * is followed by a {@link #get()} before a {@link #set},
         * {@code #get()} will call {@link #initialValue()} and create a new
         * entry with the resulting value.
         *
         * @since 1.5
         */
        public void remove() {
            Thread currentThread = Thread.currentThread();
            Values values = values(currentThread);
            if (values != null) {
                values.remove(this);
            }
        }
    
        /**
         * Creates Values instance for this thread and variable type.
         */
        Values initializeValues(Thread current) {
            return current.localValues = new Values();
        }
    
        /**
         * Gets Values instance for this thread and variable type.
         */
        Values values(Thread current) {
            return current.localValues;
        }
    
        /** Weak reference to this thread local instance. */
        private final Reference<ThreadLocal<T>> reference
                = new WeakReference<ThreadLocal<T>>(this);
    
        /** Hash counter. */
        private static AtomicInteger hashCounter = new AtomicInteger(0);
    
        /**
         * Internal hash. We deliberately don't bother with #hashCode().
         * Hashes must be even. This ensures that the result of
         * (hash & (table.length - 1)) points to a key and not a value.
         *
         * We increment by Doug Lea's Magic Number(TM) (*2 since keys are in
         * every other bucket) to help prevent clustering.
         */
        private final int hash = hashCounter.getAndAdd(0x61c88647 * 2);
    
        /**
         * Per-thread map of ThreadLocal instances to values.
         */
        static class Values {
    
            /**
             * Size must always be a power of 2.
             */
            private static final int INITIAL_SIZE = 16;
    
            /**
             * Placeholder for deleted entries.
             */
            private static final Object TOMBSTONE = new Object();
    
            /**
             * Map entries. Contains alternating keys (ThreadLocal) and values.
             * The length is always a power of 2.
             */
            private Object[] table;
    
            /** Used to turn hashes into indices. */
            private int mask;
    
            /** Number of live entries. */
            private int size;
    
            /** Number of tombstones. */
            private int tombstones;
    
            /** Maximum number of live entries and tombstones. */
            private int maximumLoad;
    
            /** Points to the next cell to clean up. */
            private int clean;
    
            /**
             * Constructs a new, empty instance.
             */
            Values() {
                initializeTable(INITIAL_SIZE);
                this.size = 0;
                this.tombstones = 0;
            }
    
            /**
             * Used for InheritableThreadLocals.
             */
            Values(Values fromParent) {
                this.table = fromParent.table.clone();
                this.mask = fromParent.mask;
                this.size = fromParent.size;
                this.tombstones = fromParent.tombstones;
                this.maximumLoad = fromParent.maximumLoad;
                this.clean = fromParent.clean;
                inheritValues(fromParent);
            }
    
            /**
             * Inherits values from a parent thread.
             */
            @SuppressWarnings({"unchecked"})
            private void inheritValues(Values fromParent) {
                // Transfer values from parent to child thread.
                Object[] table = this.table;
                for (int i = table.length - 2; i >= 0; i -= 2) {
                    Object k = table[i];
    
                    if (k == null || k == TOMBSTONE) {
                        // Skip this entry.
                        continue;
                    }
    
                    // The table can only contain null, tombstones and references.
                    Reference<InheritableThreadLocal<?>> reference
                            = (Reference<InheritableThreadLocal<?>>) k;
                    // Raw type enables us to pass in an Object below.
                    InheritableThreadLocal key = reference.get();
                    if (key != null) {
                        // Replace value with filtered value.
                        // We should just let exceptions bubble out and tank
                        // the thread creation
                        table[i + 1] = key.childValue(fromParent.table[i + 1]);
                    } else {
                        // The key was reclaimed.
                        table[i] = TOMBSTONE;
                        table[i + 1] = null;
                        fromParent.table[i] = TOMBSTONE;
                        fromParent.table[i + 1] = null;
    
                        tombstones++;
                        fromParent.tombstones++;
    
                        size--;
                        fromParent.size--;
                    }
                }
            }
    
            /**
             * Creates a new, empty table with the given capacity.
             */
            private void initializeTable(int capacity) {
                this.table = new Object[capacity * 2];
                this.mask = table.length - 1;
                this.clean = 0;
                this.maximumLoad = capacity * 2 / 3; // 2/3
            }
    
            /**
             * Cleans up after garbage-collected thread locals.
             */
            private void cleanUp() {
                if (rehash()) {
                    // If we rehashed, we needn't clean up (clean up happens as
                    // a side effect).
                    return;
                }
    
                if (size == 0) {
                    // No live entries == nothing to clean.
                    return;
                }
    
                // Clean log(table.length) entries picking up where we left off
                // last time.
                int index = clean;
                Object[] table = this.table;
                for (int counter = table.length; counter > 0; counter >>= 1,
                        index = next(index)) {
                    Object k = table[index];
    
                    if (k == TOMBSTONE || k == null) {
                        continue; // on to next entry
                    }
    
                    // The table can only contain null, tombstones and references.
                    @SuppressWarnings("unchecked")
                    Reference<ThreadLocal<?>> reference
                            = (Reference<ThreadLocal<?>>) k;
                    if (reference.get() == null) {
                        // This thread local was reclaimed by the garbage collector.
                        table[index] = TOMBSTONE;
                        table[index + 1] = null;
                        tombstones++;
                        size--;
                    }
                }
    
                // Point cursor to next index.
                clean = index;
            }
    
            /**
             * Rehashes the table, expanding or contracting it as necessary.
             * Gets rid of tombstones. Returns true if a rehash occurred.
             * We must rehash every time we fill a null slot; we depend on the
             * presence of null slots to end searches (otherwise, we'll infinitely
             * loop).
             */
            private boolean rehash() {
                if (tombstones + size < maximumLoad) {
                    return false;
                }
    
                int capacity = table.length >> 1;
    
                // Default to the same capacity. This will create a table of the
                // same size and move over the live entries, analogous to a
                // garbage collection. This should only happen if you churn a
                // bunch of thread local garbage (removing and reinserting
                // the same thread locals over and over will overwrite tombstones
                // and not fill up the table).
                int newCapacity = capacity;
    
                if (size > (capacity >> 1)) {
                    // More than 1/2 filled w/ live entries.
                    // Double size.
                    newCapacity = capacity * 2;
                }
    
                Object[] oldTable = this.table;
    
                // Allocate new table.
                initializeTable(newCapacity);
    
                // We won't have any tombstones after this.
                this.tombstones = 0;
    
                // If we have no live entries, we can quit here.
                if (size == 0) {
                    return true;
                }
    
                // Move over entries.
                for (int i = oldTable.length - 2; i >= 0; i -= 2) {
                    Object k = oldTable[i];
                    if (k == null || k == TOMBSTONE) {
                        // Skip this entry.
                        continue;
                    }
    
                    // The table can only contain null, tombstones and references.
                    @SuppressWarnings("unchecked")
                    Reference<ThreadLocal<?>> reference
                            = (Reference<ThreadLocal<?>>) k;
                    ThreadLocal<?> key = reference.get();
                    if (key != null) {
                        // Entry is still live. Move it over.
                        add(key, oldTable[i + 1]);
                    } else {
                        // The key was reclaimed.
                        size--;
                    }
                }
    
                return true;
            }
    
            /**
             * Adds an entry during rehashing. Compared to put(), this method
             * doesn't have to clean up, check for existing entries, account for
             * tombstones, etc.
             */
            void add(ThreadLocal<?> key, Object value) {
                for (int index = key.hash & mask;; index = next(index)) {
                    Object k = table[index];
                    if (k == null) {
                        table[index] = key.reference;
                        table[index + 1] = value;
                        return;
                    }
                }
            }
    
            /**
             * Sets entry for given ThreadLocal to given value, creating an
             * entry if necessary.
             */
            void put(ThreadLocal<?> key, Object value) {
                cleanUp();
    
                // Keep track of first tombstone. That's where we want to go back
                // and add an entry if necessary.
                int firstTombstone = -1;
    
                for (int index = key.hash & mask;; index = next(index)) {
                    Object k = table[index];
    
                    if (k == key.reference) {
                        // Replace existing entry.
                        table[index + 1] = value;
                        return;
                    }
    
                    if (k == null) {
                        if (firstTombstone == -1) {
                            // Fill in null slot.
                            table[index] = key.reference;
                            table[index + 1] = value;
                            size++;
                            return;
                        }
    
                        // Go back and replace first tombstone.
                        table[firstTombstone] = key.reference;
                        table[firstTombstone + 1] = value;
                        tombstones--;
                        size++;
                        return;
                    }
    
                    // Remember first tombstone.
                    if (firstTombstone == -1 && k == TOMBSTONE) {
                        firstTombstone = index;
                    }
                }
            }
    
            /**
             * Gets value for given ThreadLocal after not finding it in the first
             * slot.
             */
            Object getAfterMiss(ThreadLocal<?> key) {
                Object[] table = this.table;
                int index = key.hash & mask;
    
                // If the first slot is empty, the search is over.
                if (table[index] == null) {
                    Object value = key.initialValue();
    
                    // If the table is still the same and the slot is still empty...
                    if (this.table == table && table[index] == null) {
                        table[index] = key.reference;
                        table[index + 1] = value;
                        size++;
    
                        cleanUp();
                        return value;
                    }
    
                    // The table changed during initialValue().
                    put(key, value);
                    return value;
                }
    
                // Keep track of first tombstone. That's where we want to go back
                // and add an entry if necessary.
                int firstTombstone = -1;
    
                // Continue search.
                for (index = next(index);; index = next(index)) {
                    Object reference = table[index];
                    if (reference == key.reference) {
                        return table[index + 1];
                    }
    
                    // If no entry was found...
                    if (reference == null) {
                        Object value = key.initialValue();
    
                        // If the table is still the same...
                        if (this.table == table) {
                            // If we passed a tombstone and that slot still
                            // contains a tombstone...
                            if (firstTombstone > -1
                                    && table[firstTombstone] == TOMBSTONE) {
                                table[firstTombstone] = key.reference;
                                table[firstTombstone + 1] = value;
                                tombstones--;
                                size++;
    
                                // No need to clean up here. We aren't filling
                                // in a null slot.
                                return value;
                            }
    
                            // If this slot is still empty...
                            if (table[index] == null) {
                                table[index] = key.reference;
                                table[index + 1] = value;
                                size++;
    
                                cleanUp();
                                return value;
                            }
                        }
    
                        // The table changed during initialValue().
                        put(key, value);
                        return value;
                    }
    
                    if (firstTombstone == -1 && reference == TOMBSTONE) {
                        // Keep track of this tombstone so we can overwrite it.
                        firstTombstone = index;
                    }
                }
            }
    
            /**
             * Removes entry for the given ThreadLocal.
             */
            void remove(ThreadLocal<?> key) {
                cleanUp();
    
                for (int index = key.hash & mask;; index = next(index)) {
                    Object reference = table[index];
    
                    if (reference == key.reference) {
                        // Success!
                        table[index] = TOMBSTONE;
                        table[index + 1] = null;
                        tombstones++;
                        size--;
                        return;
                    }
    
                    if (reference == null) {
                        // No entry found.
                        return;
                    }
                }
            }
    
            /**
             * Gets the next index. If we're at the end of the table, we wrap back
             * around to 0.
             */
            private int next(int index) {
                return (index + 2) & mask;
            }
        }
    }
    
    

    相关文章

      网友评论

          本文标题:ThreadLocal源代码--read the fucking

          本文链接:https://www.haomeiwen.com/subject/opyzjttx.html