广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Java多线程之ThreadLocal原理总结
  • 844
分享到

Java多线程之ThreadLocal原理总结

Java多线程ThreadLocal 原理Java多线程ThreadLocal 原理 2023-05-15 08:05:45 844人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

目录1、什么是 ThreadLocal:2、ThreadLocal 的数据结构:3、ThreadLocal 的核心方法: 1、set()方法: 2、get()方法

1、什么是 ThreadLocal:

ThreadLocal,即线程本地变量,如果你创建了一个变量,那么访问这个变量的每个线程都会有这个变量的本地拷贝,多个线程操作这个变量的时候,实际操作自己本地内存里面的变量,从而起到线程隔离的作用,避免了线程安全问题

ThreadLocal 适用于无状态,副本变量独立后不影响业务逻辑的高并发场景,如果业务逻辑强依赖于变量副本,则不适合用 ThreadLocal 解决,需要另寻解决方案

应用场景:

数据库连接池会话管理中使用

2、ThreadLocal 的数据结构:

jdk8 中,每个线程 Thread 内部都维护了一个 ThreadLocalMap 的数据结构,ThreadLocalMap 中有一个由内部类 Entry 组成的 table 数组,Entry 的 key 就是线程的本地化对象 ThreadLocal,而 value 则存放了当前线程所操作的变量副本。每个 ThreadLocal 只能保存一个副本 value,并且各个线程的数据互不干扰,如果想要一个线程保存多个副本变量,就需要创建多个ThreadLocal。

一个 ThreadLocal 的值,会根据线程的不同,分散在 N 个线程中,所以获取 ThreadLocal 的 value,有两个步骤:

第一步,根据线程获取 ThreadLocalMap

第二步,根据自身从 ThreadLocalMap 中获取值,所以它的 this 就是 Map 的 Key

当执行 set() 方法时,其值是保存在当前线程的 ThreadLocal 变量副本中
当执行get() 方法中,是从当前线程的 ThreadLocal 的变量副本获取。

所以对于不同的线程,每次获取副本值时,别的线程并不能获取到当前线程的副本值,形成了线程的隔离,互不干扰。

3、ThreadLocal 的核心方法:

ThreadLocal 对外暴露的方法有4个:

1.initialValue()方法:返回为当前线程初始副本变量值。
2.get()方法:获取当前线程的副本变量值。
3.set()方法:保存当前线程的副本变量值。
4.remove()方法:移除当前前程的副本变量值

 1、set()方法:

// 设置当前线程对应的ThreadLocal值
public void set(T value) {
    Thread t = Thread.currentThread(); // 获取当前线程对象
    ThreadLocalMap map = getMap(t);
    if (map != null) // 判断map是否存在
        map.set(this, value); 
        // 调用map.set 将当前value赋值给当前threadLocal。
    else
        createMap(t, value);
        // 如果当前对象没有ThreadLocalMap 对象。
        // 创建一个对象 赋值给当前线程
}
 
// 获取当前线程对象维护的ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
// 给传入的线程 配置一个threadlocals
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

执行流程:

1.获得当前线程,根据当前线程获得 map。
2.如果 map 不为空,则将参数设置到 map 中,当前的 Threadlocal 作为 key。
3.如果 map 为空,则给该线程创建 map,设置初始值。

 2、get()方法:

public T get() {
    Thread t = Thread.currentThread();//获得当前线程对象
    ThreadLocalMap map = getMap(t);//线程对象对应的map
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);// 以当前threadlocal为key,尝试获得实体
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    // 如果当前线程对应map不存在
    // 如果map存在但是当前threadlocal没有关连的entry。
    return setInitialValue();
}
 
// 初始化
private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

执行流程:

(1)先尝试获得当前线程,再根据当前线程获取对应的 map
(2)如果获得的 map 不为空,以当前 threadlocal 为 key 尝试获得 entry
(3)如果 entry 不为空,返回值。
(4)如果 2 跟 3 出现无法获得,则通过 initialValue 函数获得初始值,然后给当前线程创建新 map

 3、remove()方法:

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}

执行流程:

首先尝试获取当前线程,然后根据当前线程获得map,从map中尝试删除enrty。

4、initialValue() 方法:

protected T initialValue() {
    return null;
}

执行流程:

(1)如果没有调用 set() 直接 get(),则会调用此方法,该方法只会被调用一次,
(2)默认返回一个缺省值null,如果不想返回null,可以Override 进行覆盖。

4、ThreadLocal 的哈希冲突的解决方法:线性探测

HashMap 不同,ThreadLocalMap 结构中没有 next 引用
ThreadLocalMap 中解决哈希冲突的方式并非链表的方式,而是采用线性探测的方式,当发生哈希冲突时就将步长加1或减1,寻找下一个相邻的位置

流程说明:

1.根据 ThreadLocal 对象的 hash 值,定位到 table 中的位置 i;

2.如果当前位置是 null,就初始化一个 Entry 对象放在位置 i 上;

3.如果位置 i 已经有 Entry 对象了,如果这个 Entry 对象的 key 与即将设置的 key 相同,那么重新设置 Entry 的 value;

4.如果位置 i 的 Entry 对象和 即将设置的 key 不同,那么寻找下一个空位置;

5、ThreadLocal 的内存泄露:

在使用 ThreadLocal 时,当使用完变量后,必须手动调用 remove() 方法删除 entry 对象,否则会造成 value 的内存泄露,严格来说,ThreadLocal 是没有内存泄漏问题,有的话,那也是忘记执行 remove() 引起的

内存泄露的根本原因在于 ThreadLocalMap 的生命周期与当前线程 CurrentThread 的生命周期相同,且 ThreadLocal 使用完没有进行手动删除导致的

ThreadLocal 的内存泄露与强弱引用无关,那么为什么还要用弱引用呢?

(1)Entry 中的 key(Threadlocal)是弱引用,目的是将 ThreadLocal 对象的生命周期跟线程周期解绑,用 WeakReference 弱引用关联的对象,只能生存到下一次垃圾回收之前,GC发生时,不管内存够不够,都会被回收。

(2)当我们使用完 ThreadLocal,而 Thread 仍然运行时,即使忘记调用 remove() 方法, 弱引用也会比强引用多一层保障:当 GC 发生时,弱引用的 ThreadLocal 被收回,那么 key 就为 null 了。而 ThreadLocalMap 中的 set()、get() 方法,会针对 key == null (也就是 ThreadLocal 为 null) 的情况进行处理,如果 key == null,则系统认为 value 也应该是无效了应该设置为 null,也就是说对应的 value 会在下次调用 ThreadLocal 的 set()、get() 方法时,执行底层 ThreadLocalMap 中的 expungeStaleEntry() 方法进行清除无用的 value,从而避免内存泄露。

6、ThreadLocal 的应用场景:

(1)Hibernate 的 session 获取:每个线程访问数据库都应当是一个独立的 session 会话,如果多个线程共享同一个 session 会话,有可能其他线程关闭连接了,当前线程再执行提交时就会出现会话已关闭的异常,导致系统异常。

使用 ThreadLocal 的方式能避免线程争抢session,提高并发安全性。

(2)spring事务管理:事务需要保证一组操作同时成功或失败,意味着一个事务的所有操作需要在同一个数据库连接上,Spring 采用 Threadlocal 的方式,来保证单个线程中的数据库操作使用的是同一个数据库连接,同时采用这种方式可以使业务层使用事务时不需要感知并管理 connection 对象,通过传播级别,巧妙地管理多个事务配置之间的切换,挂起和恢复

7、如果想共享线程的 ThreadLocal 数据怎么办 ?

使用 InheritableThreadLocal 可以实现多个线程访问 ThreadLocal 的值

我们在主线程中创建一个 InheritableThreadLocal 的实例,然后在子线程中得到这个InheritableThreadLocal实例设置的值。

private void test() {    
final ThreadLocal threadLocal = new InheritableThreadLocal();       
threadLocal.set("主线程的ThreadLocal的值");    
Thread t = new Thread() {        
    @Override        
    public void run() {            
      super.run();            
      Log.i( "我是子线程,我要获取其他线程的ThreadLocal的值 ==> " + threadLocal.get());        
    }    
  };          
  t.start(); 
} 

8、为什么一般用 ThreadLocal 都要用 static?

ThreadLocal 能实现线程的数据隔离,不在于它自己本身,而在于 Thread 的 ThreadLocalMap,所以,ThreadLocal 可以只实例化一次,只分配一块存储空间就可以了,没有必要作为成员变量多次被初始化。

到此这篇关于Java多线程之ThreadLocal原理总结的文章就介绍到这了,更多相关Java多线程ThreadLocal内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Java多线程之ThreadLocal原理总结

本文链接: https://www.lsjlt.com/news/208492.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java多线程之ThreadLocal原理总结
    目录1、什么是 ThreadLocal:2、ThreadLocal 的数据结构:3、ThreadLocal 的核心方法: 1、set()方法: 2、get()方法...
    99+
    2023-05-15
    Java多线程ThreadLocal 原理 Java多线程 ThreadLocal 原理
  • Java多线程之ThreadLocal的原理是什么
    今天小编给大家分享一下Java多线程之ThreadLocal的原理是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1、什...
    99+
    2023-07-06
  • Java多线程 ThreadLocal原理解析
    目录1、什么是ThreadLocal变量2、ThreadLocal实现原理3、内存泄漏问题4、使用场景1)存储用户Session2)解决线程安全的问题3)使用ThreadLocal重...
    99+
    2022-11-12
  • Java多线程之ThreadLocal浅析
    目录介绍什么是ThreadLocal?ThreadLocal 的作用是什么?如何使用ThreadLocal如何创建一个ThreadLocal实例ThreadLocal的实现原理Thr...
    99+
    2023-05-17
    Java多线程 Java ThreadLocal 多线程ThreadLocal
  • Java线程池实现原理总结
    目录一、线程池参数二、线程池执行流程三、四种现成的线程池要理解实现原理,必须把线程池的几个参数彻底搞懂,不要死记硬背 一、线程池参数 1、corePoolSize(必填):核心线程数...
    99+
    2022-11-13
  • Java多线程之Park和Unpark原理
    目录一、基本使用二、特点三、park unpark 原理四、park/unpark 原理总结一、基本使用 它们是 LockSupport 类中的方法 // 暂停当前线程 Lock...
    99+
    2022-11-12
  • Java 多线程学习总结3
    在上一篇中,我们当然希望a++,b++执行完之后,show方法再来show.我们需要的是“原子”动作,一次性地把a++,b++不间断地执行。在java中是利用“互斥”的方法,互斥谁呢?互斥的是相同对象的加锁代码。如果我们把第一篇的SomeB...
    99+
    2023-01-31
    多线程 Java
  • Java中ThreadLocal线程变量的实现原理
    目录ThreadLocal是什么ThreadLocal实现原理分析ThreadLocal内存泄漏问题ThreadLocal是什么 ThreadLocal 使得我们可以创建线程私有的变...
    99+
    2022-11-13
  • 总结java多线程之互斥与同步解决方案
    目录一、线程互斥与同步二、synchronized三、轻量锁与偏向锁一、线程互斥与同步 互斥:指的是多个线程不能同时访问共享变量 同步:指的是多个线程按指定的顺序执行操作 在同时有多...
    99+
    2022-11-12
  • Java多线程之Park和Unpark原理分析
    这篇文章主要介绍了Java多线程之Park和Unpark原理分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Java的优点是什么1. 简单,只需理解基本的概念,就可以编写适...
    99+
    2023-06-14
  • Java多线程揭秘之synchronized工作原理
    目录一.特性二.加锁过程(锁升级/锁膨胀)1.无锁状态2.偏向锁3.轻量级锁4.重量级锁5.总结三.锁优化1.锁消除2.锁粗化在学习本篇文章时,如果有不太懂的地方,大家也可以先看看博...
    99+
    2022-11-12
  • Java多线程知识点全面总结
    目录Java多线程知识点总结(1)什么是进程什么是线程?(2)多线程的运行状态(3)线程的创建和使用(4)Runnable 接口实现多线程(5)Callable接口实现多线程多线程常...
    99+
    2022-11-13
  • Java多态实现原理详细梳理总结
    目录一、概述引入定义二、多态的体现三、多态的好处四、引用类型转换向上转型向下转型为什么要转型转型的异常一、概述 引入 多态是继封装、继承之后,面向对象的第三大特性。生活中,比如跑的动...
    99+
    2022-11-13
  • Java中ThreadLocal线程变量的实现原理是什么
    这篇文章主要介绍了Java中ThreadLocal线程变量的实现原理是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java中ThreadLocal线程变量的实现原理是什么文章都会有所收获,下面我们一起来看...
    99+
    2023-07-02
  • 运用示例详细总结Java多线程
    目录进程与线程Java中线程实现的方式实现 Runnable 接口继承 Thread 类Thread 类和 Runnable 接口线程的状态变化取得和设置线程的名称线程的操作方法线程...
    99+
    2022-11-12
  • Java基础之线程锁相关知识总结
    一、 synchronized关键字 1.对象锁 a.当使用对象锁的时候,注意要是相同的对象,并且当有线程正在访问对象锁内部的代码的时候,其他线程无法访问。(注意无法访问的范围)。 ...
    99+
    2022-11-12
  • Python进阶之多线程的实现方法总结
    目录线程Python中的多线程threading.Thread () 创建线程继承 threading.Thread 类的线程创建主线程使用daemon参数控制过程使用.join()...
    99+
    2023-05-16
    Python多线程实现方法 Python多线程实现 Python多线程
  • java 多线程的几种实现方法总结
    java 多线程的几种实现方法总结1.多线程有几种实现方法?同步有几种实现方法?多线程有两种实现方法,分别是继承Thread类与实现Runnable接口同步的实现方面有两种,分别是synchronized,wait与noti...
    99+
    2023-05-30
    java 多线程 ava
  • Java多线程和并发常见问题总结
    这篇文章主要讲解了“Java多线程和并发常见问题总结”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java多线程和并发常见问题总结”吧!Java多线程问题1:进程和线程之间有什么不同一个进程...
    99+
    2023-06-17
  • Java多线程的常用创建方式总结
    Java多线程的创建方式 Java现在有四种创建的方式:继承Threa类、实现Runnable接口、实现Callable接口、线程池 Thread、Runnable都在java.la...
    99+
    2022-11-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作