ThreadLocal

1.What is ThreadLocal?

ThreadLocal,可以理解为线程本地变量。它为T类型的变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量,线程之间读写对象的操作是相互隔离、互不影响的,也就是不同线程的ThreadLocal之间是不可共享的。

/**下面是调用ThreadLocal的set方法,我们可以清晰的看到每次都会获取当前线程对象*/
public void set(T var1) {
        Thread var2 = Thread.currentThread();
        ThreadLocal.ThreadLocalMap var3 = this.getMap(var2);
        if (var3 != null) {
            var3.set(this, var1);
        } else {
            this.createMap(var2, var1);
        }

    }

/**这个方法就是上面的this.getMap(),从中我们可以知道获取的其实是是保存在Thread里面的ThreadLocalMap,这个对象其实就是Thread里面的保存副本*/
ThreadLocal.ThreadLocalMap getMap(Thread var1) {
        return var1.threadLocals;
    }

2.ThreadLocal辩析
先看一个定义:ThreadLocal类中,有一个静态内部类——ThreadLocalMap,虽然名字有Map,但并没有实现java的Map接口,其内部自定义的存储实体Entry类,继承了弱引用类WeakReference,以ThreadLocal的弱引用对象作为Entry的key,实际要存储的T类型对象,作为value。

Thread类中有一个类型为ThreadLocal.ThreadLocalMap的成员变量,名字叫threadLocals,默认为null,在进行get之前,必须先set,否则会报空指针异常。
如果不想set,则须重写initialValue方法。
当我们在程序中new一个ThreadLocal的对象local:

// 创建一个ThreadLocal
ThreadLocal<Object> local = new ThreadLocal<Object>() {
        @Override
        protected Object initialValue() {
                    return new Object(); 
        }
 };

// ThreadLocalMap 源码
static class ThreadLocalMap {
        private static final int INITIAL_CAPACITY = 16;
        /**可以看到ThreadLocalMap 内部持有一个Entry[]数组,这个数组就是用来存放变量的*/
        private ThreadLocal.ThreadLocalMap.Entry[] table;
        private int size;
        private int threshold;
		/**中间省略了很多代码*/
}

//Entry 源码,
static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;

            Entry(ThreadLocal<?> var1, Object var2) {
                super(var1);
                this.value = var2;
            }
        }

调用local.set方法设值或者在get方法中执行initialValue方法初始化值后,当前线程的threadLocals中会有一个Entry,key弱引用指向对象local,value是Object对象,内存中的结构如下图:
在这里插入图片描述

为什么这里要使用弱引用呢?

  • 首先,我们要了解引用和内存泄漏之间的关系,引用(Ref)是保存在Stack里面,而它的实例(Obj)是保存在Heap里面的,而我们使用Obj实例的时候是需要通过Ref获取的,导致内存泄漏的原因是我们将Ref=null,那么Obj就会在某些时候被GC回收,这是最理想的状态
  • 其次,我们知道一个实例对象可能会被多个对象引用持有,在上面的图中,Entry如果是强引用的话,那么我们即使将ThreadLocalRef = null,那么ThreadLocal实例对象一定会被Entry的引用持有,那么ThreadLocal实例对象一定不会释放,故而将Entry使用弱引用
  • 并且执行get和set方法的时候会回收key==null的value,这个就是使用弱引用的好处。
  • 想要解决内存泄漏那就需要我们使用完当前的Threadlocal后调用threadLocal.remove()方法
    ThreadLocal存在线程不安全的情况
    如果定义static的ThreadLocal,那么就会出现线程不安全的情况
// 如下,因为number被不同线程改变的话引用是一样的,那么就出现了线程不安全的情况了,解决的办法是去掉static或者初始化ThreadLocal新建一个对象
public static Number number = new Number(0);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值