ThreadLocal基本使用和源码分析

来源:互联网 发布:无人机航拍软件 编辑:程序博客网 时间:2024/06/01 07:53

ThreadLocal基础

1. 什么是ThreadLocal

ThreadLocal为解决多线程并发问题提供了一种便捷的方法. 当使用ThreadLocal维护变量时ThreadLocal为每一个使用该变量的线程提供一个副本. 因此每个线程在操作ThreadLocal变量时都是在操作和自己绑定的那个副本, 上述定义会在下面的源码分析阶段得到验证.

2. 接口方法

  • public T get() : 获取值.
  • public void set(T value) : 设置值
  • public void remove() : 移除值.
  • protected T initialValue() : 初始化值, 重写此方法可以设置初始值.

ThreadLocal基本使用

ThreadLocal 的使用非常简单, 类似于Map的使用, 只不过他会使用当前线程生产一个Key值.

/** * ThreadLocal基本使用. * @author WSJ */public class ThreadLocalDemo {    /**     * ThreadLocal 集合.     */    static ThreadLocal<String> mThreadLocal = new ThreadLocal<String>(){        /**         * 设置初始值.         */        protected String initialValue() {            return "我是初始值";        };    };    public static void main(String[] args) {        System.out.println("Main-01 : " + mThreadLocal.get());        mThreadLocal.set("Main--------SetData");        new ThreadA().start();        System.out.println("Main-02 : " + mThreadLocal.get());    }    /**     * 测试线程     * @author WSJ     */    public static class ThreadA extends Thread{        @Override        public void run() {            System.out.println("Child-02 : " + mThreadLocal.get());            mThreadLocal.set("ThreadA------SetData");            try {                sleep(2000);            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println("Child-02 : " + mThreadLocal.get());        }    }}

输出结果如下 :

Main-01 : 我是初始值Main-02 : Main--------SetDataChild-02 : 我是初始值Child-02 : ThreadA------SetData

从输出结果上看, 确实证实了, ThreadLocal 在每一个线程中都会有自己的一个副本. 因此修改的只是和当前变量相关的变量副本.不会影响其他的线程中的副本.

ThreadLocal源码分析

其实ThreadLocal的实现原理也十分简单. 结合源码来分析一下. 介绍ThreadLoacal源码前我们需要先来看一个类ThreadLocalMap 类. ThreadLocalMap其实就是一个自定义的Map类.

ThreadLocalMap 内部使用动态数组, 来保存数据. 用法和Map一样.部分源码如下

/** * 数据实体类, 继承自WeakReference, 弱引用. * Key : WeakReference 的值本身, * value : 是要存储的数据. */static class Entry extends WeakReference<ThreadLocal<?>> {    /** The value associated with this ThreadLocal. */    Object value;    Entry(ThreadLocal<?> k, Object v) {        super(k);        value = v;    }}/** * 动态数组 */private Entry[] table;// set 方法private void set(ThreadLocal<?> key, Object value) {    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();}// get 方法.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);}

通过上面的部分源码可以看出, ThreadLocalMap的实现和HashMap的实现类似. 可以暂时将他看成Map.只不过他的Key只能是ThreadLocal对象.

来看看ThreadLocal源码吧.

/** * ThreadLocal 源码分析 * @author  Josh Bloch and Doug Lea * @since   1.2 */public class ThreadLocal<T> {    /**     * 自定义Hash值,用来作为Key     */    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);    }    /**     * 初始化值, 如果再一个线程中没有设置ThreadLocal的值,     * 直接get()则会获取到这个方法的返回值.     */    protected T initialValue() {        return null;    }    /**     * 无参构造方法.     */    public ThreadLocal() {    }    /**     * 获取值的方法.     */    public T get() {        // 1. 获取当前线程对象.        Thread t = Thread.currentThread();        // 2. 根据当前线程对象获取到ThreadLocalMap对象.        ThreadLocalMap map = getMap(t);        if (map != null) {            // 3. 获取以当前ThreadLocal为Key的实体对象.            ThreadLocalMap.Entry e = map.getEntry(this);            // Entry的Value就是实际值.            if (e != null) {                @SuppressWarnings("unchecked")                T result = (T)e.value;                return result;            }        }        // 如果没有设置值, 则返回初始化值.        return setInitialValue();    }    /**     * 设置初始化值,     */    private T setInitialValue() {        // 获取初始化值,可以重写 initialValue() 方法来设置初始值.        T value = initialValue();        // 保存值.        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        // 判断当前的线程对象是否绑定了一个ThreadLocalMap对象,        if (map != null)            // 有直接保存数据, ThreadLocal作为Key.            map.set(this, value);        else            // 没有ThreadLocalMap对象,则创建对象.            createMap(t, value);        return value;    }    /**     * Sets the current thread's copy of this thread-local variable     * to the specified value. 设置当前线程中的ThreadLocal的副本的值.     */    public void set(T value) {        // 获取当前Thread对象.        Thread t = Thread.currentThread();        // 获取和当前ThreadLocal绑定的ThreadLocalMap对象.        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);// 直接保存        else            createMap(t, value); // 创建新的ThreadLocalMap对象.    }    /**     * Removes the current thread's value for this thread-local     * variable. 移除当前线程中ThreadLocal变量的副本值.     *     * @since 1.5     */     public void remove() {         ThreadLocalMap m = getMap(Thread.currentThread());         if (m != null)             m.remove(this);     }    /**     * 获取当前线程的ThreadLocal, ThreadLocalMap 就是一个自定一的Map类.     */    ThreadLocalMap getMap(Thread t) {        // Thread类中保存着.ThreadLocalMap对象.        return t.threadLocals;    }    /**     * 创建一个和当前ThreadLocal绑定的     */    void createMap(Thread t, T firstValue) {        t.threadLocals = new ThreadLocalMap(this, firstValue);    }}

代码中已经有了详细的注释, 接下来只是一些说明.

ThreadLocal为每一个线程提供副本的实现方法

Thread类源码片段

ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocal#createMap() 方法源码

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

通过上面提供的两个源码片段可以看出每一个线程都保存着一个ThreadLocalMap对象, 并且使用当前ThreadLocal对象作为Key值.也就是说实现的方式相当于创建多个Map集合, 使用同一个Key来保存数据,这样的话, 虽然Key值相同但是在不同的集合中可以存储不同的值.

原创粉丝点击