java ThreadLocal源码分析

写个demo测试下:

java 复制代码
private static void testThreadLocal() {
        ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
        new Thread(){
            @Override
            public void run() {
                threadLocal.set(9527);
                System.out.println("curr thread: " + Thread.currentThread().getName() + ", set 9527");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("curr thread: " + Thread.currentThread().getName() + ", getValue: " + threadLocal.get());
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                threadLocal.set(250);
                System.out.println("curr thread: " + Thread.currentThread().getName() + ", set 250");
                System.out.println("curr thread: " + Thread.currentThread().getName() + ", getValue: " + threadLocal.get());
            }
        }.start();
    }

打印:

可以看出,同一个ThreadLocal对象,在不同的线程中设置值和取值互不干扰。

研究下源码。

Thread线程对象里面维护了一个ThreadLocalMap对象,上面示例中设置的值都是存在对应线程的ThreadLocalMap对象中,ThreadLocalMap用于存储其对应线程的数据:

看下如何创建ThreadLocalMap:

可以看出,ThreadLocalMap中有个table数组,用于存储Entry(键:ThreadLocal对象,值为需要保存的对象)。 table数组初始长度只有16, 根据threadLocalHashCode & 15得到索引,该索引范围0 - 15。

后续table数组容量不够了会扩容该数组:

再看下Entry类:

继承了弱引用,当ThreadLocal对象没有强引用时可以被GC回收。

ThreadLocal核心方法:

1、set, 将需要保存的值保存到当前线程的ThreadLocalMap对象中。上面分析过,是通过键值对的方式保存,key为该ThreadLocal对象,value为要保存的对象。

2、get, 从当前线程的ThreadLocalMap中取出该ThreadLocal存的对象。

3、remove, 当存储数据不再使用时,记得调用remove方法移除数据,防止内存泄漏:

ok. 总结,通过ThreadLocal存储数据是存到调用线程中,不同线程存储的数据是互相独立的。可用于实现线程局部变量。