ThreadLocal基本使用和源码分析

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--------SetData
Child-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值相同但是在不同的集合中可以存储不同的值.

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值