做一篇人人能搞懂的ThreadLocal(源码)

导读:本篇文章讲解 做一篇人人能搞懂的ThreadLocal(源码),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

目录

 1、ThreadLocal

二、ThreadLocal与Synchronized的区别

三、ThreadLocal分析

四、ThreadLocal源码分析

Set()方法

get()方法

remove()方法

五、ThreadLocal的数据结构


引入:

我们知道在我们多线程中,如果同时访问同一共享变量,可能会出现线程安全问题,为了保证线程安全,我们常常会在访问这个共享变量的时候加锁,来达到同步效果。

做一篇人人能搞懂的ThreadLocal(源码)

可能会造成死锁,故引入 ThreadLocal

 1、ThreadLocal

ThreadLocal是JDK提供的,支持线程本地变量。也就是说,如果我们创建了一个ThreadLocal变量,则访问这个变量的每个线程都会有这个变量的一个本地副本。如果多个线程同时对这个变量进行读写操作时,实际上操作的是线程自己本地内存中的变量,从而避免了 线程安全的问题。

做一篇人人能搞懂的ThreadLocal(源码)

  • ThreadLocal 提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。ThreadLocal 变量通常被private static修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。

二、ThreadLocal与Synchronized的区别

共同点:

  • ThreadLocal和Synchonized都用于解决多线程并发访问。

区别:

  • Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离
  • Synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。

三、ThreadLocal分析

初始

做一篇人人能搞懂的ThreadLocal(源码)

 改变:增加删除ThreadLocal中的变量操作

做一篇人人能搞懂的ThreadLocal(源码)

线程A和线程B存储在ThreadLocal中的变量互不干扰,线程A存储的变量只能由线程A访问,线程B存储的变量只能由线程B访问。

四、ThreadLocal源码分析

Set()方法

public void set(T value) {
    //获取当前线程
    Thread t = Thread.currentThread();
    //以当前线程为Key,获取ThreadLocalMap对象
    ThreadLocalMap map = getMap(t);
    //获取的ThreadLocalMap对象不为空
    if (map != null)
        //设置value的值
        map.set(this, value);
    else
        //获取的ThreadLocalMap对象为空,创建Thread类中的threadLocals变量
        createMap(t, value);
}
//这里的this指的是代码中调用set的那个对象,也就是ThreadLocal对象

        从上面的代码可以看出,ThreadLocal  set赋值的时候首先会获取当前线程thread,并使用当前线程作为Key调用getMap(t)方法thread线程中的ThreadLocalMap属性。如果map属性不为空,则直接更新value值,如果map为空,则程序调用createMap(t, value)方法来实例化Thread类的threadLocals成员变量。也就是创建当前线程的threadLocals变量

  • getMap()
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

getMap(Thread t)方法获取的是线程变量自身的threadLocals成员变量
  • ThreadLocalMap ()
  static class ThreadLocalMap {
 
        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;
 
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

    }

看出ThreadLocalMap是ThreadLocal的内部静态类,而它的构成主要是用Entry来保存数据 ,而且还是继承的弱引用。在Entry内部使用ThreadLocal作为key,使用我们设置的value作为value。

  • createMap()
//这个是threadlocal 的内部方法
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
 
 
    //ThreadLocalMap 构造方法
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

get()方法

public T get() {
    //获取当前线程
    Thread t = Thread.currentThread();
    //获取当前线程的threadLocals成员变量
    ThreadLocalMap map = getMap(t);
    //获取的threadLocals变量不为空
    if (map != null) {
        //返回本地变量对应的值
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    //初始化threadLocals成员变量的值
    return setInitialValue();
}

通过当前线程来获取threadLocals成员变量,如果threadLocals成员变量不为空,则直接返回当前线程绑定的本地变量,否则调用setInitialValue()方法初始化threadLocals成员变量的值。

  • etInitialValue()
private T setInitialValue() {
    //调用初始化Value的方法
    T value = initialValue();
    Thread t = Thread.currentThread();
    //根据当前线程获取threadLocals成员变量
    ThreadLocalMap map = getMap(t);
    if (map != null)
        //threadLocals不为空,则设置value值
        map.set(this, value);
    else
        //threadLocals为空,创建threadLocals变量
        createMap(t, value);
        return value;
}
  • initialValue()
protected T initialValue() {
    return null;
}

remove()方法

public void remove() {
    //根据当前线程获取threadLocals成员变量
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        //threadLocals成员变量不为空,则移除value值
        m.remove(this);
}

remove方法,直接将ThrealLocal 对应的值从当前相差Thread中的ThreadLocalMap中删除。为什么要删除,这涉及到内存泄露的问题。

实际上 ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,弱引用的特点是,如果这个对象只存在弱引用,那么在下一次垃圾回收的时候必然会被清理掉。

所以如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候会被清理掉的,这样一来 ThreadLocalMap中使用这个 ThreadLocal 的 key 也会被清理掉。但是,value 是强引用,不会被清理,这样一来就会出现 key 为 null 的 value。

五、ThreadLocal的数据结构

做一篇人人能搞懂的ThreadLocal(源码)

  •  弱引用
    • ThreadLocalMap有自己的独立实现,可以简单地将它的key视作ThreadLocalvalue为代码中放入的值(实际上key并不是ThreadLocal本身,而是它的一个弱引用
  • 线程隔离
    • 每个线程在往ThreadLocal里放值的时候,都会往自己的ThreadLocalMap里存,读也是以ThreadLocal作为引用,在自己的map里找对应的key,从而实现了线程隔离
  • 结构
    • ThreadLocalMap有点类似HashMap的结构,只是HashMap是由数组+链表实现的,而ThreadLocalMap中并没有链表结构。

补:Java的四种引用类型

  • 强引用:我们常常new出来的对象就是强引用类型,只要强引用存在,垃圾回收器将永远不会回收被引用的对象,哪怕内存不足的时候

  • 软引用:使用SoftReference修饰的对象被称为软引用,软引用指向的对象在内存要溢出的时候被回收

  • 弱引用:使用WeakReference修饰的对象被称为弱引用,只要发生垃圾回收,若这个对象只被弱引用指向,那么就会被回收

  • 虚引用:虚引用是最弱的引用,在 Java 中使用 PhantomReference 进行定义。虚引用中唯一的作用就是用队列接收对象即将死亡的通知

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/115339.html

(0)
Java光头强的头像Java光头强
0 0

相关推荐

  • 给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度。 后端笔记

    给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度。

    0 0120
    小半的头像 小半
    2024年3月29日
  • JavaServlet 后端笔记

    JavaServlet

    0 0208
    飞熊的头像 飞熊
    2023年12月26日
  • Linux系统中查看某个命令的路径 后端笔记

    Linux系统中查看某个命令的路径

    0 0147
    飞熊的头像 飞熊
    2023年5月10日
  • Java基础-线程 后端笔记

    Java基础-线程

    0 0199
    飞熊的头像 飞熊
    2023年12月26日
  • 队列及其经典面试题 后端笔记

    队列及其经典面试题

    0 0164
    Java光头强的头像 Java光头强
    2023年2月20日
  • Flink Table Api 之表函数使用 后端笔记

    Flink Table Api 之表函数使用

    0 0203
    飞熊的头像 飞熊
    2023年5月14日
  • Oracle数据库中如何获取当前年下的01- 12 个月 后端笔记

    Oracle数据库中如何获取当前年下的01- 12 个月

    0 0303
    seven_的头像 seven_
    2023年3月3日
  • 经典SQL语句大全—基础 后端笔记

    经典SQL语句大全—基础

    0 0132
    飞熊的头像 飞熊
    2023年3月8日
  • 字符串转二维码,二维码转Base64 后端笔记

    字符串转二维码,二维码转Base64

    0 0132
    飞熊的头像 飞熊
    2023年12月26日
  • MySQL的下载、安装、配置(图文详解) 后端笔记

    MySQL的下载、安装、配置(图文详解)

    0 0109
    飞熊的头像 飞熊
    2023年7月24日
  • 配置root账户ssh免密登录并使用docker-machine构建docker服务 后端笔记

    配置root账户ssh免密登录并使用docker-machine构建docker服务

    0 0201
    飞熊的头像 飞熊
    2023年8月4日
  • 解析XML文件时的嵌套异常SAXParseException 后端笔记

    解析XML文件时的嵌套异常SAXParseException

    0 0232
    飞熊的头像 飞熊
    2023年12月11日

发表回复

登录后才能评论

扫码关注公众号,技术文章第一时间送达

做一篇人人能搞懂的ThreadLocal(源码)

站长精选

  • SpringBoot + Nacos + k8s 优雅停机

    SpringBoot + Nacos + k8s 优雅停机

    2023年12月17日

  • 我是如何使用 Spring Retry 减少1000 行代码!

    我是如何使用 Spring Retry 减少1000 行代码!

    2023年8月31日

  • Android 上剪藏文章和网页到 Obsidian 笔记库。

    Android 上剪藏文章和网页到 Obsidian 笔记库。

    2022年11月21日

  • 使用 Spring AOP 优雅的实现异步文件上传

    使用 Spring AOP 优雅的实现异步文件上传

    2023年9月23日

  • GitHub星标11k+,新一代支持多平台的免费 SSH 终端神器,太酷了!

    GitHub星标11k+,新一代支持多平台的免费 SSH 终端神器,太酷了!

    2022年12月19日

  • Arrays.asList() 隐藏的陷阱,你避开了吗?

    Arrays.asList() 隐藏的陷阱,你避开了吗?

    2023年7月20日

  • 10个解放双手实用在线工具,有些代码真的不用手写

    10个解放双手实用在线工具,有些代码真的不用手写

    2023年8月31日

  • 利用 Java 反射机制提高 SpringBoot 的代码质量

    利用 Java 反射机制提高 SpringBoot 的代码质量

    2024年4月11日

  • 为什么不用 Go 开发操作系统?

    为什么不用 Go 开发操作系统?

    2023年6月18日

  • 绝对干货,超全的 MyBatis 动态代理原理讲解!

    绝对干货,超全的 MyBatis 动态代理原理讲解!

    2022年12月12日

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!

深圳SEO优化公司松江区百度网站优化定制方案太湖网站优化推广梁子湖网站关键词排名优化如何太原家具行业网站优化推广有哪些对学校网站进行SEO优化广东网站优化优质商家邯郸专业seo网站搜索优化松岗免费的网站优化化州seo网站关键词优化东莞网站快速排名优化拉萨济南网站优化公司厦门做网站优化的公司太原家居行业网站优化推广方案达州网站优化品牌潍坊网站seo优化价格如何优化一个网站操作步骤南沙网站关键词优化费用贞丰网站优化推广公司罗湖正规网站优化方案网站排名优化宀金苹果下拉荆州品牌网站优化公司排名中山网站优化广告西宁网站优化网站的优化崇高甜柚网络光明网站关键词优化公司没有网站也能优化关键词吗连州网站关键词优化软件新绛网站优化哪家强外贸网站优化步骤威海网站优化排名歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化