java 浅谈ThreadLocal底层源码(通俗易懂)

这篇具有很好参考价值的文章主要介绍了java 浅谈ThreadLocal底层源码(通俗易懂)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、ThreadLocal类基本介绍

        1.概述 : 

        2.作用及特点 : 

二、ThreadLocal类源码解读

        1.代码准备 : 

            1.1 图示 

            1.2 数据对象

            1.3 测试类

            1.4 运行测试

        2.源码分析 : 

            2.1 set方法解读

            2.2 get方法解读


一、ThreadLocal类基本介绍

        1.概述 : 

        (1) ThreadLocal,本地线程变量,是Java中的一个类。ThreadLocal类提供了一种线程绑定机制,可以将状态与线程(Thread)关联起来。ThreadLocal类如下图所示 : 

java 浅谈ThreadLocal底层源码(通俗易懂),Java进阶,# 源码分析,java,开发语言,jvm,ThreadLocal,线程

        (2) 每个线程都会有自己独立的一个ThreadLocal变量,该变量对其他线程而言是封闭且隔离的,因此对该变量的读写操作只会影响到当前执行线程的这个变量,而不会影响到其他线程的同名变量

        2.作用及特点 : 

        (1) ThreadLocal可以实现在同一个线程数据共享,从而解决多线程数据安全问题。

        (2) 通过ThreadLocal实例的set方法,可以为当前线程关联一个数据(变量,对象,数组)

        (3) 通过ThreadLocal实例的get方法,可以像Map一样存取key-value键值对(其中key为当前线程),注意,显式的用法上与Map不相同

        (4) 每一个ThreadLocal对象,只能为当前线程关联一个数据,若想为当前线程关联多个数据,就需要使用到多个ThreadLocal实例

        (5) ThreadLocal实例往往定义为static类型。

        (6) ThreadLocal中保存的数据,会在线程销毁后自动释放


二、ThreadLocal类源码解读

        1.代码准备 : 

            1.1 图示 

                首先,我们要把代码打通,确保ThreadLocal对象可以在同一线程中实现数据共享。根据下图来定义所需要的测试类 : 

java 浅谈ThreadLocal底层源码(通俗易懂),Java进阶,# 源码分析,java,开发语言,jvm,ThreadLocal,线程

                在T1类,T1Service类,以及T2DAO类中分别打印出当前线程的名字,以及放入到threadLocal1对象中的数据对象,对比三个类打印出的线程名字和数据对象是否相同,即可验证“ThreadLocal可以实现在同一个线程数据共享”。

            1.2 数据对象

                定义Apple类和Grape类用作测试的数据对象。
                Apple类代码如下 : 

package threadlocal;

public class Apple {
}

                Grape类代码如下 : 

package threadlocal;

/**
 * @author : Cyan_RA9
 * @version : 21.0
 */
public class Grape {
}

            1.3 测试类

                T1类代码如下 : 

package threadlocal;

/**
 * @author : Cyan_RA9
 * @version : 21.0
 */
public class T1 {
    //定义一个静态的ThreadLocal对象
    public static ThreadLocal<Object> threadLocal1 = new ThreadLocal<>();

    public static void main(String[] args) {
        //在主线程中启动一个新的子线程
        new Thread(new Task()).start();
    }

    public static class Task implements Runnable{
        @Override
        public void run() {
            System.out.println("(Task)run方法,当前线程名 = " + Thread.currentThread().getName());
            Apple apple = new Apple();
            Grape grape = new Grape();

            //向threadLocal1对象中放入一个Apple对象
            System.out.println("(Task)run方法,放入的对象 = " + apple);
            threadLocal1.set(apple);

            new T1Service().test();
        }
    }
}

                T1Service类代码如下 : 

package threadlocal;

/**
 * @author : Cyan_RA9
 * @version : 21.0
 */
public class T1Service {
    public void test() {
        String name = Thread.currentThread().getName();
        System.out.println("(T1Service)当前线程名 =  " + name);

        Object o = T1.threadLocal1.get();
        System.out.println("(T1Service)得到的对象o = " + o);

        new T2DAO().test();
    }
}

                T2DAO类代码如下 : 

package threadlocal;

/**
 * @author : Cyan_RA9
 * @version : 21.0
 */
public class T2DAO {
    public void test() {
        String name = Thread.currentThread().getName();
        System.out.println("(T2DAO)当前线程名 = " + name);

        Object o = T1.threadLocal1.get();
        System.out.println("(T2DAO)得到的对象o = " + o);
    }
}

            1.4 运行测试

                运行结果 : 

java 浅谈ThreadLocal底层源码(通俗易懂),Java进阶,# 源码分析,java,开发语言,jvm,ThreadLocal,线程

        2.源码分析 : 

            2.1 set方法解读

                set方法源码如下 : 

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            map.set(this, value);
        } else {
            createMap(t, value);
        }
    }

                第一步,可以看到,set方法中首先就获取到了当前线程,而当前线程,就是调用set方法时——负责执行线程类的run方法的那个线程;说明set方法和当前线程是关联的
                第二步通过当前线程对象获取到了ThreadLocalMap对象。此处的ThreadLocalMap,是ThreadLocal类的一个静态内部类。如下图所示 : 

java 浅谈ThreadLocal底层源码(通俗易懂),Java进阶,# 源码分析,java,开发语言,jvm,ThreadLocal,线程

                注意,为什么是通过当前线程对象来获取ThreadLocalMap对象呢?
                因为当前线程持有自己的ThreadLocal对象(该对象调用了set方法),而ThreadLocalMap又是ThreadLocal的一个内部类.
                
继续,接着判断得到的map对象是否为空,如下图所示 : 

java 浅谈ThreadLocal底层源码(通俗易懂),Java进阶,# 源码分析,java,开发语言,jvm,ThreadLocal,线程

                如果不为空,就将当前的ThreadLocal对象(this,即指在T1类中一开始调用set方法的ThreadLocal对象)和该对象调用set方法时放入的数据(value,此处是放入的Apple对象)。从这里也可以看出,如果同一个ThreadLocal对象再次调用set方法,会对存入的数据(value)进行替换——即"每一个ThreadLocal对象,只能为当前线程关联一个数据"。
                如果为空,就创建一个与当前线程对象关联的ThreadLocalMap对象,并将目标数据放入(value)。

                在set方法调用处设一个断点,进入Debug界面后可以看到当前线程对象名字,如下图所示 : 

java 浅谈ThreadLocal底层源码(通俗易懂),Java进阶,# 源码分析,java,开发语言,jvm,ThreadLocal,线程

                在this对象中向下找,可以找到一个threadLocals属性,发现它就是ThreadLocalMap类型,如下图所示 : 

java 浅谈ThreadLocal底层源码(通俗易懂),Java进阶,# 源码分析,java,开发语言,jvm,ThreadLocal,线程

                我们也可以Thread类的源码中找到这个属性,如下图所示 :

java 浅谈ThreadLocal底层源码(通俗易懂),Java进阶,# 源码分析,java,开发语言,jvm,ThreadLocal,线程

                而该属性下的table, 就是实际存放数据的地方(可以看到table是ThreadLocalMap的内部类Entry类型的数组)。当set方法执行完毕后,我们可以看到table数组中的Apple对象,如下图所示 :

java 浅谈ThreadLocal底层源码(通俗易懂),Java进阶,# 源码分析,java,开发语言,jvm,ThreadLocal,线程

                实际上,table就是线程用于管理ThreadLocal实例的容器
                而table数组中每个元素的referent属性(弱引用对象),也就是ThreadLocal对象,此处可以看到与之前一致,如下 : 

java 浅谈ThreadLocal底层源码(通俗易懂),Java进阶,# 源码分析,java,开发语言,jvm,ThreadLocal,线程

            2.2 get方法解读

                get方法源码如下 : (PS : 注意此处是泛型在方法上的应用,而不是自定义泛型方法)

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

                第一步,和set方法一样,都是先得到当前的线程对象。为啥?因为只有得到了当前线程对象,才能找到它的属性threadLocals[ThreadLocal$ThreadLocalMap类型],继而找到该属性维护的table数组(ThreadLocal$ThreadLocalMap$Entry[]类型),然后根据当前线程持有的的ThreadLocal实例,找到数组中对应的Entry元素,继而拿到它的属性value(保存的数据)
                显然,get方法的源码中也的确是这么干的。通过当前线程对象拿到ThreadLocalMap对象,我们可以看一下getMap(t)的源码,如下图所示 :

java 浅谈ThreadLocal底层源码(通俗易懂),Java进阶,# 源码分析,java,开发语言,jvm,ThreadLocal,线程

                之后,判断map对象是否为空,如果不为空,就根据当前持有的ThreadLocal实例去找table数组中对应的Entry元素。继续往下走 : 

java 浅谈ThreadLocal底层源码(通俗易懂),Java进阶,# 源码分析,java,开发语言,jvm,ThreadLocal,线程

                拿到对应的Entry元素后,还要进行判断,如果该元素的确是存在的(表明当前的ThreadLocal实例已经为当前线程绑定过数据[一个value]), 就取出该Entry元素的value属性,此处为之前保存的apple对象,然后返回。

                🆗,以上就是对ThreadLocal的一些浅显解读。感谢阅读!文章来源地址https://www.toymoban.com/news/detail-695981.html

到了这里,关于java 浅谈ThreadLocal底层源码(通俗易懂)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java八股文面试[多线程]——ThreadLocal底层原理和使用场景

    源码分析: ThreadLocal中定义了ThreadLocalMap静态内部类,该内部类中又定义了Entry内部类。 ThreadLocalMap定了 Entry数组。 Set方法: Get方法: Thread中定义了两个ThreaLocalMap成员变量: Spring使用ThreadLocal解决线程安全问题  我们知道在一般情况下,只有 无状态的Bean 才可以在多线程环

    2024年02月10日
    浏览(36)
  • 七大排序算法——冒泡排序,通俗易懂的思路讲解与图解(完整Java代码)

    排序:所谓排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 上述待排序的数中,有两个5。 将 前面 的5标记一个a, 将 后面 的5标记一个b。 通过算法进行排序后,这一组数就有序了, 但是要看两个相同的5的位置是否有改变。

    2024年02月16日
    浏览(28)
  • 七大排序算法——希尔排序,通俗易懂的思路讲解与图解(完整Java代码)

    排序:所谓排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 上述待排序的数中,有两个5。 将 前面 的5标记一个a, 将 后面 的5标记一个b。 通过算法进行排序后,这一组数就有序了, 但是要看两个相同的5的位置是否有改变。

    2024年02月03日
    浏览(39)
  • 七大排序算法——堆排序,通俗易懂的思路讲解与图解(完整Java代码)

    排序:所谓排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 上述待排序的数中,有两个5。 将 前面 的5标记一个a, 将 后面 的5标记一个b。 通过算法进行排序后,这一组数就有序了, 但是要看两个相同的5的位置是否有改变。

    2024年02月16日
    浏览(23)
  • 七大排序算法——归并排序,通俗易懂的思路讲解与图解(完整Java代码)

    排序:所谓排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 上述待排序的数中,有两个5。 将 前面 的5标记一个a, 将 后面 的5标记一个b。 通过算法进行排序后,这一组数就有序了, 但是要看两个相同的5的位置是否有改变。

    2024年02月15日
    浏览(38)
  • java8新特性之toMap的用法——全网独一无二的通俗易懂的讲解

    对于java8的新特性toMap方法,相信有很多人都在工作中用过,接下来就通俗易懂的讲解一下toMap吧 先来看看官网对于toMap方法的解释 toMap有个三个重载的方法,每一个重载方法的详解分别如下 (1)方法1:两个参数 public static T, K, U Collector T, ?, MapK,U toMap(Function? super T, ? extends

    2024年02月08日
    浏览(49)
  • java 并发 随笔7 ThreadLocal源码走读

    可以看到 Thread 是内部是维护了局部变量的(thread-local-map) 很多的细节都在代码块中备注了

    2024年02月13日
    浏览(31)
  • 通俗易懂实现功能强大的实战项目 springboot+java+vue+mysql 汽车服务管理系统

    ✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java实战 | SpringBoot/SSM Python实战项目 | Django 微信小

    2024年01月16日
    浏览(29)
  • 通俗易懂实现功能强大的实战项目 springboot+java+vue+mysql 汽车租赁管理系统

    ✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java实战 | SpringBoot/SSM Python实战项目 | Django 微信小

    2024年01月19日
    浏览(37)
  • java 数据结构 ArrayList源码底层 LinkedList 底层源码 迭代器底层

    对于数据结构我这边只告诉你右边框框里的 栈的特点:后进先出,先进后出,入栈也成为压栈,出栈也成为弹栈 栈就像一个弹夹 队列先进先出后进后出 队列像排队 链表查询满 但是增删快(相对于数组而言) 拓展:还有一个双向链表 他在查询元素的时候更快些,因为他在拿到一个元素

    2024年02月05日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包