Java ThreadLocal 使用及实现原理

一. ThreadLocal是什么

ThreadLocal 是线程本地数据存储类,通过ThreadLocal可以在特定的线程中存储数据和变量, 并且这些数据之后只能由该线程访问,其他线程是访问不了的, 保证各个线程里数据和变量的独立性; 即ThreadLocal使每个线程可以访问自己内部的副本变量。

二. 通过例子来了解ThreadLocal

下面通过一个简单的例子来说明ThreadLocal的作用以及使用;
代码如下:

public class ThreadLocalDemo {

    private static ThreadLocal<String> mThreadLocalDesc = new ThreadLocal<>();
    public static class Thread1 extends Thread {
        @Override
        public void run() {
            super.run();
            mThreadLocalDesc.set("this is thread-1");
            System.out.println("Thread1  mThreadLocalDesc = "+mThreadLocalDesc.get());
        }
    }

    public static class Thread2 extends Thread {
        @Override
        public void run() {
            super.run();

            mThreadLocalDesc.set("this is thread-2");
            System.out.println("Thread2  mThreadLocalDesc = "+mThreadLocalDesc.get());
        }
    }

    public static void main(String[] args) {
        mThreadLocalDesc.set("this is main thread");
        System.out.println("main mThreadLocalDesc = "+mThreadLocalDesc.get());
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
        thread1.start();
        thread2.start();
    }
}

我们分别使用ThreadLocal来在Thread1, Thread2 和 main线程这三个线程里设置相应的变量, 并且都打印出来,检验是否保存了线程各自变量的值, 程序运行结果如下:
这里写图片描述

三.源码解析ThreadLocal类

实现思想,每个线程维护一个 ThreadLocalMap 的映射表,映射表的 keyThreadLocal 实例本身,value 是要存储的副本变量。ThreadLocal 实例本身并不存储值,它只是提供一个在当前线程中找到副本值的 key。

ThreadLocal 类定义如下:

public class ThreadLocal<T> 

从类的声明可以看出ThreadLocal是一个泛型类, 所以它可以用来存储任何类型的线程本地数据;

ThreadLocal类提供的几个方法:

public T get() { }
get 方法是用来获取 ThreadLocal 在当前线程中保存的变量副本

public void set(T value) { }
set 用来设置当前线程中变量的副本

public void remove() { }
remove 用来移除当前线程中变量的副本

protected T initialValue() { }
initialValue 是一个protected方法,一般是用来在使用时进行重写的,做初始化操作

1. 先分析set方法的实现:

public void set(T value) {
    //取得当前线程
    Thread t = Thread.currentThread();
    //获取当前线程 ThreadLocalMap 
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

Thread类的threadLocals字段定义如下:

//与此线程相关的ThreadLocal值。这个映射由ThreadLocal类维护。
ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocalMap 来存储每个线程副本变量,它是ThreadLocal 里的一个静态内部类。ThreadLocalMap 也是采用的散列表(Hash)思想来实现的

ThreadLocalMap的实现方式:

Map 是一种 key-value 形式的数据结构,ThreadLocalMap 使用 Entry 类来存储数据,下面是Entry类的定义:

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;
    Entry(ThreadLocal<?> k, Object v) {
         super(k);
         value = v;
    }
}

EntryThreadLocal 实例作为 key,副本变量作为 value 存储起来, Entry 中对于 ThreadLocal 实例的引用是一个弱引用;

整个 set 方法就是先取得当前线程,然后通过 getMap(t) 方法获取到一个MapMap的类型为ThreadLocalMap, 如果此时获取到的map = null,就是先创建ThreadLocalMap, 否则就存储该变量, 注意这里获取键值对传进去的是 this,而不是当前线程t.

2. 再分析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();
}

get方法也是先获取当前线程, 然后再获取ThreadLocalMap, 通过ThreadLocalMapgetEntry(this)直接获取value;
如果在map中查找不到对应的存储,则会通过调用setInitialValue方法返回初始化时的初始值,而默认情况下,initialValue方法返回的是null。

猜你喜欢

转载自blog.csdn.net/u010784887/article/details/79680858