快速掌握ThreadLocal

一、ThreadLocal概念

ThreadLocal是线程变量,在每个线程中都创建了一个ThreadLocalMap对象,存储当前ThreadLocal副本,该Map是以ThreadLocal对象为key,任意对象为值的存储结构,每个线程都能够访问属于自己的ThreadLocalMap存储的value。而ThreadLocal能够集中管理和操作这些Map。

类中定义的ThreadLocal实例一般会被private static修饰,这样可以让ThreadLocal实例的状态和Thread绑定在一起

二、ThreadLocalMap

没看源码之前我以为ThreadLocalMap是实现了Map接口,但是源码中的ThreadLocalMapThreadLocal的内部类,内部的Entry也是独立实现,在ThreadLocalMap中,也是用Entry来保存K-V结构数据的。但是Entry中key只能是ThreadLocal对象,value可以是其他任何对象

static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
  • Thread中的threadLocals变量指向的是一个map,这个map就是ThreadLocal.ThreadLocalMap,里面存放的是跟当前线程绑定的ThreadLocal变量。同样还有一个ThreadLocal.ThreadLocalMap类型的变量——inheritableThreadLocals里面也是存放的ThreadLocal变量,但是存放的是从当前线程的父线程继承过来的ThreadLocal变量。

  • 既然是Map那么肯定要解决Hash冲突,由于ThreadLocalMap结构简单,没有next引用,即她没有使用链表的方式来解决冲突,而是使用线性探测的方式,根据初始的key的hashCode确定元素在table中的位置,如果发生冲突即该位置已经被占用,且是其他key值,那么寻找一定步长的下一个位置,直到能够找到存放的位置。ThreadLocalMap采用的步长就是1,加一或者减一。

/**
 * Increment i modulo len.
 */
private static int nextIndex(int i, int len) {
    return ((i + 1 < len) ? i + 1 : 0);
}

/**
 * Decrement i modulo len.
 */
private static int prevIndex(int i, int len) {
    return ((i - 1 >= 0) ? i - 1 : len - 1);
}

三、ThreadLocal中的方法

3.1、ThreadLocal是如何被线程使用的

在这里插入图片描述
线程中的栈上存放的是Thread引用ThreadLocal引用,Thread引用会引用一个ThreadLocalMap对象

ThreadLocal中有几个核心方法:

public T get()
public void set(T value)
public void remove()

这几个方法都是对静态内部类ThreadLocalMap进行操作,根据当前线程来获取map,对map进行操作,比如set():

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

set方法先获取当前线程,然后创建一个ThreadLocalMap对象并接受当前线程中的map,判断是否为空,不为空则覆盖之前的值,为空则重新创建一个ThreadLocalMap并绑定到当前线程的threadLocals

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

其他方法与之类似,比较容易懂,可以自行查看源码。

四、ThreadLocal使用举例

/**
 * @Description:
 * @Author: Mt.Li
 * @Create: 2020-07-23 21:11
 */
public class ThreadLocalDemo {
    public static class MyRunnable implements Runnable {

        // jdk1.8之后提供的新的初始化方法
        // ThreadLocal<Integer> threadLocal1 = ThreadLocal.withInitial(() -> 0);

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

        DataTest dataTest = new DataTest();

        @Override
        public void run() {
            threadLocal.set(dataTest.num);
            threadLocal.set(threadLocal.get() + 1);
            System.out.println("dataTest中的num值为:" + dataTest.num);
            System.out.println(Thread.currentThread().getName() + "中的ThreadLocal's num为:" + threadLocal.get());
        }

    }

    private static class DataTest{
        private Integer num = 0;
    }

    public static void main(String[] args) throws InterruptedException {
        MyRunnable threadLocalDemo = new MyRunnable();

        Thread thread1 = new Thread(threadLocalDemo, "thread 1");
        Thread thread2 = new Thread(threadLocalDemo, "thread 2");
        Thread thread3 = new Thread(threadLocalDemo, "thread 3");

        thread1.start();
        thread2.start();
        thread3.start();

        thread1.join();
        thread2.join();
        thread3.join();
    }
}

运行结果:

dataTest中的num值为:0
thread 1中的ThreadLocal's num为:1
dataTest中的num值为:0
thread 2中的ThreadLocal's num为:1
dataTest中的num值为:0
thread 3中的ThreadLocal's num为:1

可以看到,线程与线程之间互不影响,只对属于自己的value进行操作,对传入的用来初始话的对象或者变量,是不会进行改变的。

以上全为个人理解,如有不对,请及时指出

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值