Java学习笔记(2)—ThreadLocal

Java学习笔记(2)—ThreadLocal

不积小流无以成江海,不积跬步无以至千里

一、概念

ThreadLocal 是一个关于创建线程局部变量的类,主要作用是做数据隔离,保存到 ThreadLocal 中的数据只属于当前线程,该数据对其他线程而言是隔离的。即使用 ThreadLocal 保存的数据只能被当前线程访问,其他线程无法访问和修改。在多线程环境下,防止自己的变量被其他线程篡改。

注意:ThreadLocal 设计的目的就是为了能够在当前线程中有属于自己的变量,并不是为了解决并发或者共享变量的问题。

1.1 场景

public class Main {

    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        // 主线程设置值
        threadLocal.set("value1");
        System.out.println(Thread.currentThread().getName() + " = " + threadLocal.get());
        // 子线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 子线程获取的值是:null
                System.out.println(Thread.currentThread().getName() + " = " + threadLocal.get());
            }
        }).start();

    }
}

场景1:主线程初始化了一个 ThreadLocal 对象 threadLocal,并通过 threadLocal.set() 方法保存了一个值:“value1”,然后使用 threadLocal.get() 拿到设置的值。其中,子线程也使用 threadLocal.get() 去拿值。

问题:取值为null;

public class Main {

    private static ThreadLocal<User> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        // 主线程设置值
        User user = new User();
        threadLocal.set(user);
        System.out.println(Thread.currentThread().getName() + " = " + threadLocal.get());
        // 子线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                threadLocal.set(user);
                // 获取到的对象地址是一样的,如果对它的值进行了修改,那么其他线程拿到的值也会改变。
                System.out.println(Thread.currentThread().getName() + " = " + threadLocal.get());
            }
        }).start();

    }
}

场景2:把变量换成是一个共享的对象保存到 ThreadLocal 中,那么多个线程的 ThreadLocal.get() 取得的还是这个共享对象本身;

问题:还是有并发访问问题。

三、源码分析

3.1 Set方法

public void set(T value) {
  // 获取当前线程
  Thread t = Thread.currentThread();
  // 利用当前线程获取一个 ThreadLocalMap 的对象
  ThreadLocalMap map = getMap(t);
  // 如果上面获取的 ThreadLocalMap 对象不为空,则设置值,否则创建这个 ThreadLocalMap 对象并设置值
  if (map != null)
    map.set(this, value);
  else
    createMap(t, value);
}

  /**
  * Create the map associated with a ThreadLocal. Overridden in
  * InheritableThreadLocal.
  *
  * @param t the current thread
  * @param firstValue value for the initial entry of the map
  */
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

从 set 源码中,我们得知 ThreadLocalMap 是利用当前线程 Thread 作为参数获取的。源码如下:

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

其实,上面的代码获取的是 Thread 对象的 threadLocals 变量。源码如下:

public class Thread implements Runnable {
    省略其他内容...

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    省略其他内容...
}

结论:可以知道 ThreadLocal 的数据是放入了当前线程的一个ThreadLocalMap 实例中,key 就是 ThreadLocal 对象本身,所以只能在本线程中访问,其他线程无法访问,从而实现了数据隔离。

3.2 get方法

public T get() {
    // 获取当前线程
    Thread t = Thread.currentThread();
    // 获取 ThreadLocalMap 对象
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // 以 ThreadLocal 对象本身作为 key,获取值
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    // 如果 ThreadLocalMap 对象不存在,就设置初始值并返回
    // 从下面 setInitialValue() 的源码可知,设置的初始值是一个 null
    return setInitialValue();
}

/**
  * Variant of set() to establish initialValue. Used instead
  * of set() in case user has overridden the set() method.
  *
  * @return the initial value
  */
private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

protected T initialValue() {
    return null;
}

主要工作原理

  • Thread 类中维护着一个 ThreadLocalMap 类型的成员变量
  • ThreadLocalMap 是一个定义在 ThreadLocal 类中的内部类,是一个 map,用Entry 来进行数据存储。
  • 当调用 ThreadLocal 的 set() 方法时,先获取当前线程的 ThreadLocalMap 对象,然后以 ThreadLocal 对象作为 key 往ThreadLocalMap 中设置值。
  • 当调用 ThreadLocal 的 get() 方法时,也是先获取当前线程的 ThreadLocalMap 对象,以 ThreadLocal 对象作为 key 从 ThreadLocalMap 中获取值。
  • ThreadLocal 本身并不存储值,它只是作为一个 key 来让线程在ThreadLocalMap 中获取或设置值。

参考

Star Zheng 《聊一聊Java中的ThreadLocal》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值