ThreadLocal源码解析

用途:为了解决多线程环境下共享变量冲突问题,为每一个线程提供一个本地变量。

用法:

public class ThreadLocalDemo {
   private static final ThreadLocal<Integer> threadLocal = new ThreadLocal<>();

    public static void method(){
        Integer value;
        try {
            value = threadLocal.get();
            System.out.println(Thread.currentThread().getName()+"获取值:"+value);
        } finally {
            threadLocal.remove();
        }

    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i <5; i++) {
            final  int value = i;
            new Thread(()->{
                threadLocal.set(value);
                method();
            },"线程"+i).start();
        }
        Thread.yield();
    }
}

ThreadLocal主要就是三个方法:

set():为线程创建本地变量

get():获取本地变量

remove():移除本地变量

经典场景就是spring中对事务的处理,以及spring整合mybatis对sqlSession的应用。

源码解析:

在Thread这个类中有一个成员变量ThreadLocal.ThreadLocalMap threadLocals = null,这个map就是用来存储线程的本地变量的。接下来我们来研究一下Thread与ThreadLocal以及ThreadLocalMap三者之间的关系。

通过这张图就可以很直观的看出这三者的关系:每一个Thread中都有一个Map容器ThreadLocalMap,map中是由Entry[]数组组成的数据结构,Entry的key就是ThreadLocal,value就是我们设置的值。这其中有两个点需要我们关注。

  1. ThreadLocalMap如何解决hash冲突:

HashMap中是通过链地址法解决hash冲突的,ThreadLocalMap是通过开放定址法,基本思想是,出现冲突后按照一定算法查找一个空位置存放,根据算法不同又可分为线性探测再散列、二次探测在散列、伪随机探测在散列。

线性探测在散列即依次向后查找,二次探测在散列;即依次向前后查找,增量为1、2、3的二次方;伪随机,就是随机生成一个增量位移。

ThreadLocalmap就是采用的线性探测在散列:

2、弱引用在ThreadLocal中的应用

通过上面这段代码我们可以看出Entry类继承自WeakRefrence,构造函数中将变量k传递到了父类构造函数,可以理解为threadLocal被一个弱引用指向,若一个对象单独被弱引用指向时,该对象实例在gc时会被回收,这就是弱引用在ThreadLocal中的应用。但为什么这里要用弱引用而不用强引用?

这是一张使用ThreadLocal时的引用关系图,ThreadLocal常常会被问道内存泄漏,内存泄露可以理解为通过程序无法访问这个实列,但这个实列却无法被gc,这种情况下就发生了内存泄露。上图中的value就是内存泄漏的对象。但其实ThreadLocal内存泄露概率是很低的,需要满足一下几点

  1. 在栈中的强引用不在指向ThreadLocal,当发生gc时因为是弱引用,所以ThreadLocal会被回收,此时key==null。

  1. 线程不在被使用但线程未被销毁。若线程被销毁,那么整个ThreadMap也会被回收;若线程被复用,ThreadLocalMap中当使用get()、set()、remove()方法时会判断存不存在key==null的情况,若存在会将value释放,此时value也会被回收。

结合上面的可以得出:若key由弱引用改为强引用,那么就不会出现key==null的情况,也就不会出现再次使用set()、get()、remove()会判断key是否等于null。此时value只有在线程被销毁的情况下才会被回收。使用弱引用是为了降低内存泄露的风险。

但如果把栈中的引用改为static类型,此时也不会发生内存泄露,但最好是在使用后记得remove()。

##InheritableThreadLocal##

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值