• ThreadLocal线程局部变量


    1.原理

            ThreadLocal是用来保存当前线程数据的,每一个线程的内部都有一个ThreadLocalMap,当前这个map中存储了以当前ThreadLocal,具体的数据的一个个Entry对象。

                    

            为什么非得以ThreadLocal对象作键呢?因为一个线程可能使用了不止一个ThreadLocal对象,如果以当前线程对象做键,再去找对应的ThreadLocal就很麻烦,产生混淆。

            由于每一个线程都有属于自己的ThreadLocal线程局部变量,所以很好的实现了线程之间的数据隔离ThreadLocal中保存的数据仅属于当前线程)。       

    2.ThreadLocal中的常见方法:

            (1)存储数据        set()

            (2)获取数据        get()

            (3)清除数据         remove()

            那么问题来了,具体是怎样利用ThreadLocalMap查找数据的呢?不论是set()、get()、remove()等方法对当前map进行操作时,最终都定位到了通过计算出的下标来操作。

            而这个下标是通过哈希算法计算得到的,操作ThreadLocalMap是以当前的ThreadLocal作key,通过当前的key的HashCode值和Entry[]数组长度-1“&”运算,来计算出实际操作位置的下标,从而达到访问元素的目的。        

            作按位与运算的效果和利用哈希值直接进行取余%运算一样,但效率大大提高

    3.如何实现父子线程共享数据?

            利用了jdk提供ThreadLocal的子类InheritableThreadLocal来实现。

    1. public class ThreadLocalTest {
    2. public static void main(String[] args) {
    3. InheritableThreadLocal threadLocal = new InheritableThreadLocal();
    4. threadLocal.set("风萧萧兮易水寒");
    5. System.out.println("main主线程:"+threadLocal.get());
    6. Thread thread = new Thread(new Runnable() {
    7. @Override
    8. public void run() {
    9. System.out.println("子线程:"+threadLocal.get());
    10. }
    11. });
    12. thread.start();
    13. }
    14. }

            main函数为父线程,创建了一个thread子线程,利用InheritableThreadLocal这个子类来共享父线程的数据。

            父线程子线程均输出“风萧萧兮易水寒”。

    4.ThreadLocal如何避免内存泄露

            执行完相关的业务逻辑后,最终在finally代码块中都会调用remove()方法,将当前map中的ThreadLocal键置为空,value置为空,从而在垃圾回收的时候及时回收无用数据。

    5.应用

            (1) 线程的数据隔离

            因为ThreadLocal对象只属于当前线程,那么ThreadLocal中的数据也属于当前线程,在多线程并发的情况下,很好的实现了不同线程的数据隔离,避免了采用synchronized锁机制来保证线程安全而导致的性能上的代价。

            例如:SqlSession会话对象绑定,避免多个线程使用同一个SqlSession对象,由于关闭导致异常。

    1. //当前线程的线程局部变量
    2. private static final ThreadLocal threadSession= new ThreadLocal();
    3. public static SqlSession getSession(){//获取session会话方法
    4. SqlSession s = (SqlSession)threadSession.get();//通过仅属于当前线程的threadSession对象来获取
    5. if(s==null){//为空
    6. s = getSqlSessionFactory().openSqlSession();//则重新建立会话
    7. threadSession.set(s);//并存到ThreadLocalMap中去
    8. }
    9. }

            (2)跨函数调用

            数据通常用于同一个类中的传递,如果利用方法传递势必要关心方法的返回值类型及参数,但利用ThreadLocal可以直接实现获取,这样做还达到了解耦的效果。

            例如:RequestContextHolder源码就有很好的体现

    1. @Nullable
    2. public static RequestAttributes getRequestAttributes() {
    3. // 获取当前线程中的存储的Request Attribute
    4. //直接通过ThreadLocal对象来获取
    5. RequestAttributes attributes = requestAttributesHolder.get();
    6. if (attributes == null) {
    7. attributes = inheritableRequestAttributesHolder.get();
    8. }
    9. return attributes;
    10. }
    11. private static final ThreadLocal requestAttributesHolder = new NamedThreadLocal<>("Request attributes");
    12. private static final ThreadLocal inheritableRequestAttributesHolder = new NamedInheritableThreadLocal<>("Request context");

    以上就是我对ThreadLocal线程区域对象的粗略理解,欢迎诸君共同探讨。

  • 相关阅读:
    2022牛客暑期多校训练营3(ACFGJ)
    vue脚手架及常用指令集合
    Python3出现的Error总结
    基于Android的旅游管理系统 微信小程序
    离线数仓搭建_04_zookeeper-flume-kafka框架配置
    通过包管理器方式安装 Node.js
    .net----数组和指针
    SSM之spring注解式缓存redis
    自动化办公更简单了:新版python-office,有哪些更新?
    全栈的自我修养 ———— js中的拖拽api
  • 原文地址:https://blog.csdn.net/weixin_62226624/article/details/132966854