ThreadLocal源码分析

来源:互联网 发布:赢时胜软件 编辑:程序博客网 时间:2024/06/06 09:06

本篇文章将结合ThreadLocal源码深入探究ThreadLocal内部实现,期待对ThreadLocal有更透彻的了解。

类定义

先看看ThreadLocal类的定义:

package java.lang;public class ThreadLocal<T> {    /**     * Creates a thread local variable.     * @see #withInitial(java.util.function.Supplier)     */    public ThreadLocal() {    }}

set方法

ThreadLocal 的set方法如下:

public void set(T value) {    Thread t = Thread.currentThread();    ThreadLocalMap map = getMap(t);    if (map != null)        map.set(this, value);    else        createMap(t, value);}

getMap方法:

ThreadLocalMap getMap(Thread t) {    return t.threadLocals;}

createMap方法:

void createMap(Thread t, T firstValue) {    t.threadLocals = new ThreadLocalMap(this, firstValue);}


在ThreadLocal的set方法时,首先通过ThreadLocal.getMap(Thread t)方法获取一个和当前线程相关的ThreadLocalMap,并将其赋值给ThreadLocalMap map变量,如果获取到的ThreadLocalMap对象不为空,则调用ThreadLocalMap 的set方法将value保存起来,否则通过createMap方法创建。

Thread类threadLocals 变量

在Thread 类内部有一个ThreadLocal.ThreadLocalMap 类型的变量 inheritableThreadLocals,如下:

package java.lang;public class Thread implements Runnable {    /*     * InheritableThreadLocal values pertaining to this thread. This map is     * maintained by the InheritableThreadLocal class.     */    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;}

ThreadLocalMap类

ThreadLocalMap是ThreadLocal的一个静态内部类,它的结构如下:

static class ThreadLocalMap {    static class Entry extends WeakReference<ThreadLocal<?>> {        /** The value associated with this ThreadLocal. */        Object value;        Entry(ThreadLocal<?> k, Object v) {            super(k);            value = v;        }    }    private static final int INITIAL_CAPACITY = 16;    private Entry[] table;    private int size = 0;    private int threshold; // Default to 0    ...    /**     * 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);    }    /**     * 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;            }        }    }}

get()方法

接下来再看一下ThreadLocal类中的get()方法:

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();}

再来看setInitialValue()方法:

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;    }

再看看 initialValue 方法

protected T initialValue() {        return null;}

remove方法

最后看看ThreadLocal类中的remove()方法

/**     * 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);     }

总结

ThreadLocal实现线程隔离的核心,就在于ThreadLocalMap这个类。ThreadLocalMap是ThreadLocal类的一个静态内部类,它实现了键值对的设置和获取(类似HashMap),每个线程中都有一个独立的ThreadLocalMap副本,它所存储的值,只能被当前线程读取和修改。ThreadLocal类通过操作每一个线程特有的ThreadLocalMap副本,从而实现了变量访问在不同线程中的隔离。

0 0
原创粉丝点击