java源码解析--(ThreadLocal)

ThreadLocal介绍

ThreadLocal,很多人都叫它做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多。

可能很多朋友都知道ThreadLocal为变量在每个线程中都创建了一个副本,那样每个线程可以访问自己内部的副本变量。

这句话从表面上看起来理解正确,但实际上这种理解是不太正确的。

  1. 多线程并发执行时,需要数据共享,因此才有了volatile变量解决 多线程间的数据可见性,

  2. 也有了锁的同步机制,使变量或代码块在某一时该,只能被一个线程访问,确保共享数据的正确性。(Synchronized用于线程间的数据共享的)

  3. 多线程并发执行时,并不是所有数据都需要共享的,这些不需要共享的数据,让每个线程去维护就OK了,ThreadLocal就是用于线程间的数据隔离的。如都写重量级的对象不需要每次都创建,本身不是线程安全的,但是又要多线程访问,采用线程副本降低对象的创建数量

深入解析ThreadLocal类

先我们来看一下ThreadLocal类是如何为每个线程创建一个变量的副本的。
先看下get方法的实现:
在这里插入图片描述

第一句是取得当前线程,然后通过getMap(t)方法获取到一个map,map的类型为ThreadLocalMap。

然后接着下面获取到Entry键值对,注意这里获取Entry时参数传进去的是 this,即ThreadLocal实例,而不是当前线程t。如果获取成功,则返回value值。

如果map为空,则调用setInitialValue方法返回value。

在getMap中,是调用当期线程t,返回当前线程t中的一个成员变量threadLocals,类型为ThreadLocalMap。

这里意味着每一个线程都自带一个ThreadLocalMap成员变量。

在这里插入图片描述
可以看到ThreadLocalMap的Entry继承了WeakReference,并且使用ThreadLocal作为键值。

也就是说WeakReference封装了ThreadLocal,并作为了ThreadLocalMap的Entry的Key。

总结一下,在每个线程Thread内部有一个ThreadLocalMap类型的成员变量threadLocals,

这个ThreadLocalMap成员变量的Entry的Key为,当前ThreadLocal变量的WeakReference封装,value为变量。

为何ThreadLocalMap的键值为ThreadLocal对象? 因为每个线程中可能需要有多个threadLocal变量,也就是ThreadLocalMap里面可能会有多个Entry。

在每个线程内部 第一次调用ThreadLocal.get方法时,都会返回Null。因为默认情况下,initialValue方法返回的是null。

null 赋给(强转) 基本数据类型时会抛的空指针,null赋给 引用类型没问题。

可以在ThreadLocal的构造函数重写initialValue()方法。如下

ThreadLocal longLocal = new ThreadLocal(){

    protected Long initialValue() {
        return Thread.currentThread().getId();
    };
};

或者在调用ThreadLocal.get方法之前,需要先执行set(),以保证threadlocals中有值。

或者value为引用类型变量,null赋给 引用类型没问题。

ThreadLocal的内存泄露问题:

当使用线程池来复用线程时,一个线程使用完后并不会销毁线程,那么 分发的那个实例会一直绑定在这个线程上。

由于WeakReference封装了ThreadLocal,并作为了ThreadLocalMap的Entry的Key。如果在某些时候ThreadLocal对象被赋Null的话,弱引用会被GC收集,这样就会导致Entry的Value对象找不到,

线程被复用后如果有调用ThreadLocal.get/set方法的话,方法里面会去做遍历清除 以[ThreadLocal=Null ]为Key的Entry; 但如果一直没调用ThreadLocal.get/set方法的话就会导致内存泄漏了。

所以一般线程用完ThreadLocal后,要调用threadLocal.remove(); 如下

public static void close() {  
        // 获取当前线程内共享的Connection  
        Connection conn = threadLocal.get();  
        try {  
            // 判断是否已经关闭  
            if(conn != null && !conn.isClosed()) {  
                // 关闭资源  
                conn.close();  
                // 移除Connection  
                threadLocal.remove();  
                conn = null;  
            }  
        } catch (SQLException e) {  
            // 异常处理  
        }  
    } 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值