JUC——深入理解 ThreadLocal 与源码分析 以及 概念掌握 (案例实战——ThreadLocal结合ZooKeeper实现分布式锁 )

JUC——深入理解 ThreadLocal 与源码分析 以及 概念掌握 (案例实战——ThreadLocal结合ZooKeeper实现分布式锁 ),第1张

📖本文目录
  • 📖 ThreadLocal 详解
    • 📑 ThreadLocal 简介
    • 📑 ThreadLocal 与 Synchronized 区别
    • 📑 ThreadLocal 的基本使用
    • 📑 ThreadLocal 的原理
      • 🔖 ThreadLocal 的 set() 方法
      • 🔖 什么是ThreadLocalMap
      • 🔖 ThreadLocal 的 get() 方法
      • 🔖 ThreadLocal 的 remove()方法
        • 📚 内存泄漏问题
      • 🔖 Thread 、 ThreadLocalMap 与 ThreadLocal 三者关系
    • 📑 ThreadLocal 实用场景
      • 🔖 基于ZooKeeper 与 ThreadLocal 实现的分布式锁

本文是参考了 史上最全ThreadLocal 详解 的绝大部分知识点,以及针对其细致部分源码进行理解的笔记,如有侵权请通知俺 俺会删除的~

📖 ThreadLocal 详解 📑 ThreadLocal 简介

什么是ThreadLocal ?

ThreadLocal:顾名思义——线程变量 ,也就是说 ThreadLocal 中的填充变量是属于当前线程的(Local) ,变量是当前线程所独占的。 ThreadLocal 为变量在每一个线程当中都创建好了副本 ,每个线程都可以访问自己内部的副本变量。

📚注意点:

同一个ThreadLocal所包含的对象,在不同的Thread中有不同的副本

  • 每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这是也是 ThreadLocal 命名的由来
  • 每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题

ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景

📑 ThreadLocal 与 Synchronized 区别

ThreadLocal 是与线程绑定的一个变量

共同点

ThreadLocal 与 Synchronized 都用于解决多线程并发访问

本质区别

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

💥 本质理解

ThreadLocal里面存东西就是向它里面的Map存东西的,然后ThreadLocal把这个Map挂到当前的线程底下,这样Map就只属于这个线程了。

📑 ThreadLocal 的基本使用

ThreadLocalDemo.java

/***
 * @author: Alascanfu
 * @date : Created in 2022/4/14 17:19
 * @description: ThreadLocalDemo
 * @modified By: Alascanfu
 **/
public class ThreadLocalDemo {
    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    
    static void print(String str){
        // Prints the value of a local variable in local memory in the current thread
        System.out.println(str + " : "+threadLocal.get());
        // Clears variables in local memory
        threadLocal.remove();
    }
    
    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            ThreadLocalDemo.threadLocal.set("local_A");
            print("A");
            System.out.println("after remove :" + threadLocal.get());
        },"A").start();
        
        TimeUnit.SECONDS.sleep(1);
    
        new Thread(()->{
            ThreadLocalDemo.threadLocal.set("local_B");
            print("B");
            System.out.println("after remove :" + threadLocal.get());
        },"B").start();
    }
}

执行结果

A : local_A
after remove :null
B : local_B
after remove :null

📚 总结:

上述程序分别获取了自己线程存放的变量,他们之间变量的获取并不会错乱,这就是数据隔离性的体现。

📑 ThreadLocal 的原理 🔖 ThreadLocal 的 set() 方法

源码分析

  • 首先我们通过 Thread.currentThread() 获取当前线程t
  • 然后我们通过调用getMap()方法获取了线程t中的属性ThreadLocalMap
    • 如果threadLocalMap 不为空 ,则直接更新要保存的变量值
    • 反之创建 threadLocalMap 对象并赋值
🔖 什么是ThreadLocalMap

源码分析

  • ThreadLocalMapThreadLocal 中的一个静态内部类

  • Entry 类作为 ThreadLocalMap的保存数据类,同时是继承了WeakReference 弱引用

  • 在Entry内部使用ThreadLocal作为key,使用我们设置的value作为value

createMap()方法

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

ThreadLocalMap构造方法

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    // 初始化 Entry 数组大小 为 INITIAL_CAPACITY = 16 
    table = new Entry[INITIAL_CAPACITY];
    // 计算哈希进行数组下标映射 将 firstKey 通过 threadLocalHashCode 计算哈希进行映射下标值 i
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    // 将对应的 Entry 对象赋值到指定数组下标
    table[i] = new Entry(firstKey, firstValue);
    // 设置大小
    size = 1;
    // 设置临界值 16 * 2 / 3 
    setThreshold(INITIAL_CAPACITY);
}
🔖 ThreadLocal 的 get() 方法

源码分析

  • 首先获取得到 当前的线程t ,然后通过 当前线程t 获取threadLocalMap
    • 如果 threadLocalMap 不为空时,通过 threadLocalMap 去获取当前的 Entry 数据结构 ,这个 Entry 是通过计算 ThreadLocal 的映射下标从 table[] 中获取得到的。
      • 如果Entry 对象不为空 就将结果 value 直接返回
  • 反之上述会通过 setInitialValue() 方法自行设置初始值 null
🔖 ThreadLocal 的 remove()方法

源码分析

  • 获取得到 ThreadLocalMap ,将当前的 ThreadLocal 对应的值 从 ThreadLocalMap中删除
📚 内存泄漏问题

为什么要删除?

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

  • 所以 如果 ThreadLocal 没有被外部强引用的话,垃圾回收时就会被清理掉,这样一来 ThreadLocalMap 中的 key(弱引用的ThreadLocal) 也会被清理掉,但是 value 是强引用,不会被清理掉,这样一来就出现了key 为 null 的 value

  • 如果ThreadLocal中直接或间接包装了集合类或复杂对象,每次在同一个ThreadLocal中取出对象后,再对内容做 *** 作,那么内部的集合类和复杂对象所占用的空间可能会开始持续膨胀。

🔖 Thread 、 ThreadLocalMap 与 ThreadLocal 三者关系

简要理解

ThreadLocalMap 实际上是 Thread 的一个属性值,而 ThreadLocal 是维护 ThreadLocalMap这个属性值的一个工具类Thread线程可以拥有多个 ThreadLocal 维护自己线程独享的共享变量(这个共享变量只是针对于自己线程里面共享的)

📑 ThreadLocal 实用场景 🔖 基于ZooKeeper 与 ThreadLocal 实现的分布式锁

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/722107.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-26
下一篇 2022-04-26

发表评论

登录后才能评论

评论列表(0条)

保存