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


    目录

     1、ThreadLocal

    二、ThreadLocal与Synchronized的区别

    三、ThreadLocal分析

    四、ThreadLocal源码分析

    Set()方法

    get()方法

    remove()方法

    五、ThreadLocal的数据结构


    引入:

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

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

     1、ThreadLocal

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

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

    二、ThreadLocal与Synchronized的区别

    共同点:

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

    区别:

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

    三、ThreadLocal分析

    初始

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

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

    四、ThreadLocal源码分析

    Set()方法

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

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

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

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

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

    get()方法

    1. public T get() {
    2. //获取当前线程
    3. Thread t = Thread.currentThread();
    4. //获取当前线程的threadLocals成员变量
    5. ThreadLocalMap map = getMap(t);
    6. //获取的threadLocals变量不为空
    7. if (map != null) {
    8. //返回本地变量对应的值
    9. ThreadLocalMap.Entry e = map.getEntry(this);
    10. if (e != null) {
    11. @SuppressWarnings("unchecked")
    12. T result = (T)e.value;
    13. return result;
    14. }
    15. }
    16. //初始化threadLocals成员变量的值
    17. return setInitialValue();
    18. }

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

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

    remove()方法

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

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

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

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

    五、ThreadLocal的数据结构

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

    补:Java的四种引用类型

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

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

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

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

  • 相关阅读:
    设计模式之状态模式
    Linux下加密库Libsodium 使用实践(ip监听、封装的加密消息、运行系统命令)
    程序员工作压力大,年轻人放弃互联网了?
    开源WebRTC库放大器模式在采集桌面图像时遇到的DPI缩放与内存泄漏问题排查
    2023年湘潭大学OJ作业2 2023年下学期《C语言》作业0x01-数学计算 XTU OJ 1080,1081,1082,1083,1084
    2023最新SSM计算机毕业设计选题大全(附源码+LW)之java我的课堂642o1
    echarts利用graphic属性给饼图添加内圈图片及外圈图片(可自适应宽度位于饼图中心)
    华为机试 - 不含101的数
    一道数学题,让芯片巨头亏了5亿美金!
    windows版本-idea中下载的java版本在哪
  • 原文地址:https://blog.csdn.net/weixin_51309915/article/details/125463836