多线程-ThreaLocal-线程局部变量学习

定义

从Java官方文档中的描述:ThreadLocal类用来提供线程内部的局部变量。这种变量在多线程环境下访问(通过get和set方法访问)时能保证各个线程的变量相对独立于其他线程内的变量。ThreadLocal实例通常来说都是private static类型的,用于关联线程和线程上下文。 

作用:提供线程内局部变量,不同线程之间互不干扰,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或组件之间一些公共变量传递的复杂度。

换一句话就是为每一个使用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。

ThreadLocal几个常用方法

方法声明描述
ThreadLocal()创建ThreadLocal对象的方法
public void set(T value)设置当前线程绑定的局部变量
public T gset()获取当前线程绑定的局部变量
public void remove()移除当前线程绑定的局部变量

示例比较

感受下ThreadLocal线程局部变量(线程隔离的特点)

1.正常遍历打印时,会出现线程不隔离的情况,线程1取到线程4的内容。

2.使用ThreadLocal遍历时,解决了线程不隔离的问题。线程和内容一一对应。

3.Synchronized同步方式解决线程不隔离的问题,但是并发性降低了。

@Slf4j
public class ThreadLocalDec {

    public ThreadLocalDec(){}

    private String content;

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public static void main(String[] args) {
        // 正常遍历
        ThreadLocalDec dec1 = new ThreadLocalDec();
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                dec1.setContent("线程" + Thread.currentThread().getName() + "的内容");
                log.info("--------------------------------------------------------");
                log.info("线程" + Thread.currentThread().getName() + " \t dec1 = " + dec1.getContent());

            }, String.valueOf(i)).start();
        }

        // 线程局部遍历 ThreadLocal
        ThreadLocal<String> threadLocal = new ThreadLocal<>();
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                threadLocal.set("线程" + Thread.currentThread().getName() + "的内容");
                log.info("--------------------------------------------------------");
                log.info("线程" + Thread.currentThread().getName() + " \t  threadLocal = " + threadLocal.get());

            }, String.valueOf(i)).start();
        }

        // 同步锁 Synchronized 并发性降低
        ThreadLocalDec dec2 = new ThreadLocalDec();
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                synchronized (ThreadLocalDec.class) {
                    dec2.setContent("线程" + Thread.currentThread().getName() + "的内容");
                    log.info("--------------------------------------------------------");
                    log.info("线程" + Thread.currentThread().getName() + " \t dec2 = " + dec2.getContent());
                }
            }, String.valueOf(i)).start();

        }
    }
}

ThreadLocal和Synchronized的区别

虽然ThreadLocal模式与Synchronized关键字都用于处理多线程并发访问变量的问题,不过两者处理问题的角度和思路不同。

 ThredLocalSynchronized
原理ThreadLocal采用了以时间换空间的方式,为每一个线程都提供一个变量副本,实现同时访问而互不干扰同步机制采用以空间换时间的方式,只提供一份变量,让不同的线程排队访问。
侧重点多线程让每一个线程的数据都相互隔离多个线程之间访问资源的同步

ThreadLocal的优势(在一定的场景)

传递数据:保存每个线程绑定的数据,在需要的地方可以直接获取,避免参数直接传递带来的代码耦合问题。

线程隔离:各线程之间的数据相互隔离却又具备并发性,避免同步方式带来的性能损失。

比如获取数据库连接:

ThreadLocal<Connection> tl = new ThreadLocal();
public static Connection getConnection() {
    // 直接获取当前线程绑定的连接对象
    Connection conn = tl.get();
    if(conn == null) {
        // 去连接池中获取连接
        conn = ds.getConnection();
        tl.set(conn);
    }
    return conn;
}

ThreadLocal源码理解

 

我们主要关注下set() get() remove() setInitialValue()四个方法。

下面给出代码注释,勿喷

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.
     *
     * 线程局部变量依赖于每个线程附加的每线程线性探测哈希映射(Thread.threadLocals和
     * InheritableShreadLocals)。ThreadLocal对象充当键,
     * 通过threadLocalHashCode进行搜索。这是一个自定义哈希代码
     * (仅在ThreadLocalMaps中有用),在连续构造的ThreadLocalMaps
     * 被同一线程使用的情况下,它消除了冲突,
     * 而在不常见的情况下,它仍然表现良好。
     */
    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}.
     *
     * 返回此thread局部变量的当前线程的“初始值”。此方法将在线程第一次使用{@link#get}方法访问变量时调用,
     * 除非线程先前调用了{@link}set}方法,在这种情况下,{@code initialValue}方法将不会为线程调用。
     * 通常,每个线程最多调用一次此方法,但如果后续调用{@link#remove},然后调用{@link}get},
     * 则可以再次调用该方法。
     *
     *
     *
     * <p>This implementation simply returns {@code null}; if the
     * programmer desires thread-local variables to have an initial
     * value other than {@code null}, {@code ThreadLocal} must be
     * subclassed, and this method overridden.  Typically, an
     * anonymous inner class will be used.
     *
     *
     * <p>此实现只返回{@code null};如果程序员希望线程局部变量的初始值不是{@code null},
     * 则{@code ThreadLocal}必须子类化,并重写此方法。通常,将使用匿名内部类
     *
     * @return the initial value for this thread-local
     * 此线程本地的初始值
     * (备注:该方法是一个protected的方法,显然是为了让子类覆盖而设计的)
     */
    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 <S> 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 ThreadLocal.SuppliedThreadLocal<>(supplier);
    }

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

    /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * 返回此thread局部变量的当前线程副本中的值。如果变量对于current线程没有值,
     * 它首先被初始化为通过调用{@link}initialValue}方法返回的值。
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        // 获取当前线程对象
        Thread t = Thread.currentThread();
        // 获取此线程对象中维护的ThreadLocalMap对象
        ThreadLocal.ThreadLocalMap map = getMap(t);
        // 判断NULL
        if (map != null) {
            // 以当前线程对象为Key,调用getEntry()获取对应的存储实体e
            ThreadLocal.ThreadLocalMap.Entry e = map.getEntry(this);
            // 判断存储实体e
            if (e != null) {
                @SuppressWarnings("unchecked")
                // 获取存储实体e对应的Value,即为当前线程对应的ThreadLocal对象
                T result = (T)e.value;
                return result;
            }
        }
        /**
         * 如果map为null,则初始化
         * 两种情况
         * 第一种情况:map不存在,标识当前线程没有维护的ThreadLocalMap对象
         * 第二种情况:map存在,但是没有与当前的ThreadLocal关联的Entry
         */
        return setInitialValue();
    }

    /**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * 当前线程初始化ThreadLocal.ThreadLocalMap
     *
     * @return the initial value
     */
    private T setInitialValue() {
        // 获取初始化的值,此方法可以被子类重写,如果不重写默认返回NULL
        T value = initialValue();
        // 获取当前线程对象
        Thread t = Thread.currentThread();
        // 获取ThreadLocalMap
        ThreadLocal.ThreadLocalMap map = getMap(t);
        // 判断NULL
        if (map != null)
            // 存在则set当前线程对象Entry
            map.set(this, value);
        else
            // 如果当前线程不存在ThreadLocalMap对像,则调用createMap创建ThreadLocalMap对象,
            // 并将t(当前线程对象), value(t对应的值)作为第一个Entry放入到ThreadLocalMap中
            createMap(t, value);
        // 返回设置的值
        return value;
    }

    /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * 将此线程局部变量的当前线程副本设置为指定值。大多数子类不需要*重写这个方法,
     * 只依赖{@link#initialValue}方法来设置线程局部变量的值。
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     *        要存储在当前线程的this thread local副本中的值。
     */
    public void set(T value) {
        // 获取当前线程对象
        Thread t = Thread.currentThread();
        // 获取ThreadLocalMap
        ThreadLocal.ThreadLocalMap map = getMap(t);
        if (map != null)
            // 存在则set当前线程对象Entry
            map.set(this, value);
        else
            // 如果当前线程不存在ThreadLocalMap对像,则调用createMap创建ThreadLocalMap对象,
            // 并将t(当前线程对象), value(t对应的值)作为第一个Entry放入到ThreadLocalMap中
            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.
     *
     * 删除此线程局部变量的当前线程值。如果此线程局部变量随后被当前线程{@linkplain get read},
     * 则其值将通过调用其{@link#initialValue}方法重新初始化,除非它的值在此期间是当前线程的
     * {@linkplain#set}。这可能导致在当前线程中多次调用{@code initialValue}方法
     *
     * 删除当前线程对象中维护的ThreadLocalMap对象
     *
     * @since 1.5
     */
    public void remove() {
        // 获取当前线程对象中的ThreadLocalMap对象
        ThreadLocal.ThreadLocalMap m = getMap(Thread.currentThread());
        // 验证
        if (m != null)
            // 以当前ThreadLocal对象为Key删除对应的实体Entry
            m.remove(this);
    }

    /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocal.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 ThreadLocal.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 ThreadLocal.ThreadLocalMap createInheritedMap(ThreadLocal.ThreadLocalMap parentMap) {
        return new ThreadLocal.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 {

        /**
         * 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.
         *
         * 这个散列映射中的条目扩展了WeakReference,使用它的主ref字段作为键
         * (它总是一个*ThreadLocal对象)。注意空键(即。入口.get()*==null)
         * 表示不再引用该键,因此可以从表中删除项。在下面的代码中,这些条目被称为“过时条目”。
         *
         * Entry继承WeakRefefence,并且用ThreadLocal作为key.
         * 如果key为nu11(entry.get()==nu11),意味着key不再被引用,
         * 因此这时候entry也可以从table中清除。
         *
         * Entry继承WeakReference,也就是key(ThreadLocal)是弱引用,
         * 其目的是将ThreadLocal对象的生命周期和线程生命周期解绑。
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. 与此ThreadLocal关联的值 */
            Object value;

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

        /**
         * The initial capacity -- MUST be a power of two.
         * 他的初始容量——必须是2的幂次方。
         */
        private static final int INITIAL_CAPACITY = 16;

        /**
         * The table, resized as necessary.
         * table.length MUST always be a power of two.
         *
         * 存放数据的table,根据需要调整大小。表格长度一定是2的幂次方。
         */
        private ThreadLocal.ThreadLocalMap.Entry[] table;

        /**
         * The number of entries in the table.
         * 数组里面entrys的个数,可以用于判断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.
         * 设置resize threshold以在最坏的情况下保持2/3的负载系数。
         */
        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.
         *
         * firstmap(最初包含一个新的键值)。ThreadLocalMaps是延迟构造的,
         * 因此只有在至少有一个条目要放入时才创建。
         */
        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new ThreadLocal.ThreadLocalMap.Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new ThreadLocal.ThreadLocalMap.Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

        /**
         * Construct a new map including all Inheritable ThreadLocals
         * from given parent map. Called only by createInheritedMap.
         *
         * 构造一个新的映射,包括给定父映射中所有可继承的ThreadLocals。仅由createInheritedMap调用。
         *
         * @param parentMap the map associated with parent thread.
         */
        private ThreadLocalMap(ThreadLocal.ThreadLocalMap parentMap) {
            ThreadLocal.ThreadLocalMap.Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new ThreadLocal.ThreadLocalMap.Entry[len];

            for (int j = 0; j < len; j++) {
                ThreadLocal.ThreadLocalMap.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);
                        ThreadLocal.ThreadLocalMap.Entry c = new ThreadLocal.ThreadLocalMap.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 ThreadLocal.ThreadLocalMap.Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            ThreadLocal.ThreadLocalMap.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 ThreadLocal.ThreadLocalMap.Entry getEntryAfterMiss(ThreadLocal<?> key, int i, ThreadLocal.ThreadLocalMap.Entry e) {
            ThreadLocal.ThreadLocalMap.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.

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

            for (ThreadLocal.ThreadLocalMap.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 ThreadLocal.ThreadLocalMap.Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

        /**
         * Remove the entry for key.
         */
        private void remove(ThreadLocal<?> key) {
            ThreadLocal.ThreadLocalMap.Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            for (ThreadLocal.ThreadLocalMap.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) {
            ThreadLocal.ThreadLocalMap.Entry[] tab = table;
            int len = tab.length;
            ThreadLocal.ThreadLocalMap.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 ThreadLocal.ThreadLocalMap.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) {
            ThreadLocal.ThreadLocalMap.Entry[] tab = table;
            int len = tab.length;

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

            // Rehash until we encounter null
            ThreadLocal.ThreadLocalMap.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;
            ThreadLocal.ThreadLocalMap.Entry[] tab = table;
            int len = tab.length;
            do {
                i = nextIndex(i, len);
                ThreadLocal.ThreadLocalMap.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() {
            ThreadLocal.ThreadLocalMap.Entry[] oldTab = table;
            int oldLen = oldTab.length;
            int newLen = oldLen * 2;
            ThreadLocal.ThreadLocalMap.Entry[] newTab = new ThreadLocal.ThreadLocalMap.Entry[newLen];
            int count = 0;

            for (int j = 0; j < oldLen; ++j) {
                ThreadLocal.ThreadLocalMap.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() {
            ThreadLocal.ThreadLocalMap.Entry[] tab = table;
            int len = tab.length;
            for (int j = 0; j < len; j++) {
                ThreadLocal.ThreadLocalMap.Entry e = tab[j];
                if (e != null && e.get() == null)
                    expungeStaleEntry(j);
            }
        }
    }
}

以上

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值