> 文章列表 > 如何理解ThreadLocal

如何理解ThreadLocal

如何理解ThreadLocal

ThreadLocal的基本概念

在并发编程中,多个线程访问同一个变量,可能会出现线程安全问题、为了保证在多线程环境下访问共享变量的安全性,通常在访问共享变量的时候加锁,以实现线程同步的效果。
使用同步锁机制保证多线程访问共享变量的安全性的原理如下图。该机制能够保证同一时刻只有一个线程访问共享变量,从而确保在多线程环境下访问共享变量的安全性。
如何理解ThreadLocal
另外,为了更加灵活地确保线程的安全性,JDK中提供了一个ThreadLocal类,ThreadLocal类能够支持本地变量。在使用ThreadLocal类访问共享变量时,会在每个线程的本地内存中保存一份副本。在多个线程同时对这个变量进行读写操作时,实际操作的时本地内存中的副本,多个线程之间互不干扰,从而避免了线程安全的问题。使用ThreadLocal访问共享变量的示意图如下:
如何理解ThreadLocal

ThreadLocal的核心原理

ThreadLocal能够保证每个线程操作的都是本地内存中的变量副本。在底层实现上,调用ThreadLocal的set()方法会将本地变量保存在具体线程的内存空间中,ThreadLocal并不负责存储具体的数据。

Thread源码

在Thread类的源码中,定义了两个ThreadLocal.ThreadLocalMap类型的成员变量,分别为threadLocals和inheritableThreadLocals.

    ThreadLocal.ThreadLocalMap threadLocals = null;ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

在Thread类中定义成员变量threadLocals和inherittableThreadLocals,两者的初始值都为null,并且只有当线程第一次调用ThreadLocal或者InheritableThreadLocals的set()方法或者get()方法是才会实例化变量。
通过ThreadLocal为每个线程保存的本地变量不是存在ThreadLocal实例中的,而是存在调用线程的threadLocals变量中的。也就是说,调用ThreadLocal的set()方法存储的本地变量在具体线程的内存空间中,而ThreadLocal类支提供了set()和get()方法来存储和读取本地变量的值,当调用ThreadLocal类的set()方法时,把要存储的值存储在调用线程的threadLocals变量中,当调用ThreadLocal类的get()方法时,从当前线程的threadLocals变量中获取保存的值。

set()方法

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

从ThreadLocal类中的set()方法的源码可以看出,在set()方法中,会先获取调用set()方法的线程,然后使用当前线程对象作为key调用getMap()方法获取ThreadLocalMap对象,getMap()方法源码如下:

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

可以看出调用getMap()方法获取的就是当前线程中定义的threadLocals成员变量。获取ThreadLocalMap对象后,判断该对象是否为空,不为空则把value设置到Thread类的threadLocals成员变量中。保存数据时传递的key为当前ThreadLocal的this对象,而传递的value为调用set()方法传递的值。
如果当前线程的ThreadLocals成员变量为空,则调用createMap()方法来实例化当前线程的threadLocals成员变量,并保存value值。createMap()方法源码如下:

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

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()方法通过调用getMap()方法并传入当前线程来获取threadLocals成员变量,然后判断当前线程的threadLocals成员变量是否为空。如果不为空,则直接返回当前线程threadLocals成员变量中存储的本地变量的值。如果为空,则调用setInitialValue()方法初始化threadLocals成员变量的值。setInitialValue()的源码如下:

 private T setInitialValue() {T value = initialValue();Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);return value;}

setInitialValue()方法与set()方法大致相同,只不过setInitialValue()方法会先调用initialValue()方法来初始化value的值,最后返回value的值。initialValue()方法源码如下:

    protected T initialValue() {return null;}

initialValue()方法直接返回null,方法的具体逻辑会交由ThreadLocal类中的子类实现。

remove()方法

     public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this);}

根据调用的getMap()方法获取当前线程的threadLocals成员变量,如果当前线程的threadLocals成员变量不为空,则直接从当前线程的threadLocals成员变量中移除当前threadLocal对象对应的value值。