【并发】第二篇 ThreadLocal详解

本文深入剖析Java中的ThreadLocal机制,包括ThreadLocal简介、源码解析,重点探讨ThreadLocalMap的内部实现,如get、set、remove方法,以及内存泄漏问题及其解决方案。此外,文章还列举了ThreadLocal在数据库连接、事务管理、用户身份认证和线程安全计数器等场景的应用。
摘要由CSDN通过智能技术生成


在这里插入图片描述

管网上的介绍是这样:
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently ini alized copy of the variable. ThreadLocal instances are typically private sta c fields in classes that wish to associate state with a thread (e.g., a user ID or Transac on ID).
此类提供线程局部变量。这些变量与正常的对应变量的不同之处在于,每个访问一个变量(通过其get或set方法)的线程都有自己的、独立初始化的变量副本。ThreadLocal实例通常是类中的私有静态字段,这些字段希望将状态与线程(例如,用户ID或Transac-on ID)相关联。

一. ThreadLocal 简介

ThreadLocal 是 Java 中的一个类,它提供了线程局部变量的机制。线程局部变量是指每个线程都有自己独立的变量副本,线程之间互不影响。
通常情况下,如果多个线程同时访问一个共享变量,需要进行同步来保证线程安全,如Synchronized。而使用 ThreadLocal 也可以避免线程安全问题,因为每个线程都拥有自己的变量副本,且可以独立地操作自己的变量副本,而不会影响其他线程的副本。线程之间的变量副本互不干扰,保证了线程安全。
在这里插入图片描述

二. ThreadLocal 源码解析

ThreadLocal 类接口很简单,有4个核心内部成员方法

1. get

返回当前线程所对应的线程局部变量

public T get() {
   
    // 获取当前线程Thread对象
    Thread t = Thread.currentThread();
    // 获取当前线程的ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    if (map != null) {
   
        // 通过ThreadLocal对象获取变量副本
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
   
            @SuppressWarnings("unchecked")
            // 返回变量副本
            T result = (T)e.value;
            return result;
        }
    }
    // 如果ThreadLocalMap为空,或者变量副本未找到,则调用initialValue()方法初始化变量副本
    // 返回一个null
    return setInitialValue();
}

ThreadLocalMap 虽然是ThreadLocal的静态内部类, 但在Thread类中也有一个这样类型成员变量,也就是说真正实例化ThreadLocalMap是在Thread类中实现的, 所以getMap()方法实际上返回的是Thread类中成员变量

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

2. set

设置当前线程的线程局部变量的值

public void set(T value) {
   
     // 获取当前线程Thread对象
     Thread t = Thread.currentThread();
     // 通过Thread对象获取ThreadLocalMap对象
     ThreadLocalMap map = getMap(t);
     if (map != null)
         // 如果ThreadLocalMap对象存在,则直接设置变量副本
         map.set(this, value); 
     else
         // 如果ThreadLocalMap对象不存在,则创建一个新的ThreadLocalMap对象,并设置变量副本
         createMap(t, value);
  }

3 .remove

将当前线程局部变量的值删除, 目的是为了减少内存的占用, 避免内存泄漏
该方法是 JDK 5.0 新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收, 所以显式调用该方法清除线程的局部变量并不是必须的操作, 但它可以加快内存回收的速度。

public void remove() {
   
    //获取当前线程的ThreadLocalMap 
    ThreadLocalMap m = getMap(Thread.currentThread());
     if (m != null)
         //this指当前ThreadLocal实例
         //ThreadLocalMap中的Entry[]数组中,删除Entry[]数组中使用当前ThreadLocal实例作为key的Entry
         m.remove(this);
 }

4. initialValue

返回该线程局部变量的初始值
该方法是一个 protected 的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用 get() 或 set(Object)时才执行,并且仅执行1次。ThreadLocal 中的缺省实现直接返回一 个null。

protected T initialValue() {
   
     return null;
}

三. ThreadLocalMap 源码分析

每个线程所拥有的变量的副本数是不定的,有些线程可能有一个,有些线程可能有 2个甚至更多, 则线程内部存放变量副本需要一个容器, 而且容器要支持快速存取, 所以在每个线程内部都可以持有一个 Map 来支持多个变量 副本,这个Map被称为ThreadLocalMap。

static class ThreadLocalMap {
   
  
   /**
    * Entry数组,用于存储ThreadLocal与变量副本之间的映射关系
    */
   static class Entry extends WeakReference<ThreadLocal<?>> {
   
       //变量副本
       Object value;

       //key-ThreadLocal实例  value-变量副本
       Entry(ThreadLocal<?> k, Object v) {
   
           super(k);
           value = v;
       }
   }
   // 数组的大小必须为 2 的次幂
   private static final int INITIAL_CAPACITY = 16;
   
   // Entry对象数组,用于存储ThreadLocal与变量副本之间的映射关系
   private Entry[] table;
   
   // 记录当前数组中实际存在元素个数
   private int size = 0;

   // ThreadLocalMap的阈值,当size超过该值时进行扩容操作
   private int threshold;

   // 计算下一个数组大小的阈值
   private void setThreshold(int len) {
   
       threshold = len * 2 / 3;
   }

1. 构造方法

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
   
     //创建一个初始容量为16的数组
     table = new Entry[INITIAL_CAPACITY];
     //确定key在数组中的索引值index -- 哈希再取模
     int i 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搬砖界的小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值