【多线程】ThreadLocal详解

一、概述

ThreadLocal是Java语言提供的一种支持线程隔离的类,用它来定义变量可以隔绝其他线程对同变量的修改。

既然是隔离线程影响的,用局部变量不可以吗?
答案是可以解决的,但是当我们的程序方法调用很深的时候,我们不断的进行方法的传递就会显得很痛苦,并且参数过长不符合各大公司的编程规范。如果改成静态变量又会线程间共享,因此我们用到了ThreadLocal。

二、ThreadLocal使用

static ThreadLocal<String> tl = new ThreadLocal<>();

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                tl.set("hello");
                System.out.println("第0个线程写入成功");
            }
        }).start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("第一个线程读取到的内容:" + tl.get());
                tl.set("world");
                System.out.println("第一个线程自己写入后读取的内容:" + tl.get());
            }
        }).start();

    }

输出结果如下:

第0个线程写入成功
第一个线程读取到的内容:null
第一个线程自己写入后读取的内容:world

我们发现在第0个线程写入数据后,第一个线程读取不到第0个线程写入的hello。而第一个线程写入world后却可以读取到自己线程写入的数据。

三、线程隔离的原理

为了找到是怎么实现的线程隔离,我们首先找到了set方法。

	public void set(T value) {
        Thread t = Thread.currentThread();
        //获取当前线程的ThreadMap
        ThreadLocalMap map = getMap(t);
        if (map != null)
        	//将ThreadLocal对象作为key,保存的对象作为value
            map.set(this, value);
        else
            createMap(t, value);
    }
	//threadLocals是Thread类中的一个变量
	ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

通过上边的代码我们发现,每个线程都会维护一个ThreadLocalMap的一个集合。我们存储数据时,是存储在当前线程维护的map集合中的value上的,key为ThreadLocal对象。

看完了set方法。我们猜测get方法应该是在自己线程上通过map取值。为了验证猜测,看下jdk8中源码。

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();
    }

忽略ThreadLocalMap的实现,我们看到get的时候,也是取到当前线程下的map,然后调用底层数据蹭够Entry通过this(也就是ThreadLocal)取出对应的值。看到这里应该就明白ThreadLocal实现线程封闭的原理了吧。

四、ThreadLocal常见面试问题

1、ThreadLocal中Entry是弱引用,为什么

ThreadLocal

关于java中的引用,可以看下我之前的博文java中的引用
问我们为什么这里用到弱引用,那么我们假设这里是强引用会怎样,强引用在根可达情况下不会回收内存,当我们的threadLocalRef的引用置空的时候,我们线程中依旧有强引用,就会导致ThreadLocal实例在堆上无法释放,造成内存泄露。而我们用弱引用的话,虽然key这里是弱引用,但是threadLocalRef是强引用,依旧不会被回收掉。

2、ThreadLocal中内存泄露

我们在ThreadLocal中造成内存泄露的主要就是ThreadLocal对象以及我们存储的实际数据,即Entry的key和value指向的对象。key我们通过弱引用的方式。但是当ThreadLocal被回收的时候,我们实际的对象由于有value的指向不会被回收,但是我们在ThreadLocal的get和set方法中都会去删除key为null的键,从而回收实际的对象。
还存在一个问题,当我们把ThreadRef置为null的时候,如果长期不进行set或者get操作,同样会造成实际保存的对象无法被回收,我们可以主动执行remove方法进行回收value指向的实际对象。

3、ThreadLocal运用在哪些地方

spring框架的事务管理

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值