ThreadLocal简介与原理

一、引入场景

1. 打印方法执行的耗时

public void service(){
       before();
       doSomething();
       after();
}

2. 在before和after记录当前时间,两者相减得到doSomething()的耗时

private long startTime;  // 定义变量开始时间

public void before(){
      startTime=System.CurrentTimeMills();  // 记录开始时间
}

public void doSomething();

public void after(){
     endTime=System.CurrentTimeMills();  // 记录结束时间

     costTime=endTime-startTime;  // 计算耗时
}

3. 多线程下,共享变量开始时间startTime可能会被别的线程改写,不是线程安全的

4. 多线程解决方法:

a. 加锁,性能不好

b. ThreadLocal

二、概念

1. ThreadLocal线程本地变量,即把共享变量拷贝一份放在当前线程内,变成线程私有的变量

2. 变量的生命周期在当前线程范围内,别的线程不能访问,线程间数据隔离

3. 场景:当一个变量需要与线程关联,并且线程后面还会用到该值,比如traceId

三、ThreadLocal原理

1. Thread类里有一个变量threadLocals

2. threadLocals变量的类型是ThreadLocalMap

3. ThreadLocalMap也是一个Map,这个Map名字叫做Entry, key是ThreadLocal类的实例,value是设置的值

4. 如果要存储多个值,需要多个ThreadLocal,但建议一个线程只存一个变量,多个变量会引起hash冲突

Thread.ThreadLocalMap<ThreadLocal, Object>

4. 代码

private static ThreadLocal<Long> startTimeThreadLocal=new ThreadLocal<>(); // 创建ThreadLocal对象

public static void before(){
       startTimeThreadLocal.set(System.currentTimeMills()); // 设置值,在当前线程里,threadLocals变量Map的key为startTimeThreadLocal,value为System.currentTimeMills()
}

public static void doSomething(){

}

public static void after(){
       long endTime=System.currentTimeMills();

       long costTime=endTime-startTimeThreadLocal.get();
}

5. threadLocal.set()源码

public void set(T value) {
        Thread t = Thread.currentThread(); // 获取当前线程
        ThreadLocalMap map = getMap(t);  // set的时候生成一个ThreadLocalMap
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
}

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue); // 创建ThreadLocalMap时,设置当前线程的threadLocals变量,把threadLocal对象作为key
}

四、ThreadLocal内存泄露问题

1. ThreadLocalMap的Entry(即key+value)中,key是ThreadLocal对象,值是Object对象

2. Entry继承WeakReference弱引用,只有key是弱引用,value不是,所以每次GC都会回收key的对象

static class Entry extends WeakReference<ThreadLocal> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal k, Object v) {
        super(k);
        value = v;
    }
}

3. 当线程没有结束,比如在线程池中的线程不会停止,但是ThreadLocal已经被回收,即key的对象被回收,而value的对象没被回收,对象会占用内存,则可能导致线程中存在ThreadLocalMap<nullObject>的键值对,造成内存泄露

4. ThreadLocalMap在get/set时检查Entry,如果key为null,进行删除,可以减少内存泄漏,但还是会存在内存泄漏风险

五、最佳实践

1. JDK建议ThreadLocal定义为static,类的所有实例都可以共享这个ThreadLocal,避免了产生浪费

2. static变量的生命周期与类一样,只要类存在,static变量也存在,所以必须要remove,防止内存泄漏

3. 在finally里主动调用ThreadLocal的remove()方法清除线程共享变量

六、Netty中的FastThreadLocal,性能是jdk中的ThreadLocal的3倍

参考:

https://mp.weixin.qq.com/s?__biz=Mzg2NzA4MTkxNQ==&mid=2247485096&amp;idx=1&amp;sn=a1c7606296e7cb08d440933e1109a140&source=41#wechat_redirect

https://www.cnblogs.com/xzwblog/p/7227509.html

https://www.jianshu.com/p/98b68c97df9b

https://www.cnblogs.com/yxysuanfa/p/7125761.html

原文地址:https://www.cnblogs.com/june0816/p/7063902.html

时间: 2024-08-07 09:24:08

ThreadLocal简介与原理的相关文章

Java ThreadLocal 简介

ThreadLocal在Spring中发挥着重要的作用,在管理request作用域的Bean.事务管理.任务调度.AOP等模块都出现了它们的身影,起着举足轻重的作用.要想了解Spring事务管理的底层技术,ThreadLocal是必须攻克的山头堡垒. 我们知道spring通过各种模板类降低了开发者使用各种数据持久技术的难度.这些模板类都是线程安全的,也就是说,多个DAO可以复用同一个模板实例而不会发生冲突.我们使用模板类访问底层数据,根据持久化技术的不同,模板类需要绑定数据连接或会话的资源.但这

Android的消息机制之ThreadLocal的工作原理

提到消息机制大家应该都不陌生,在日常开发中不可避免地要涉及到这方面的内容.从开发的角度来说,Handler是Android消息机制的上层接口,这使得开发过程中只需要和Handler交互即可.Handler的使用过程很简单,通过它可以轻松地将一个任务切换到Handler所在的线程中去执行.很多人认为Handler的作用是更新UI,这说的的确没错,但是更新UI仅仅是Handler的一个特殊的使用场景,具体来说是这样的:有时候需要在子线程中进行耗时的IO操作,这可能是读取文件或者访问网络等,当耗时操作

Appium简介及原理

1.Appium简介 Appium是一个开源.跨平台的,适用于原生或混合移动应用(hybrid mobile apps)的自动化测试平台.Appium使用WebDriver(JSON wire protocol)驱动安卓和iOS移动应用.Appium的设计哲学是不要为了移动端的自动化测试而重新发明轮子,重新写一套惊天动地的api,也就是说webdriver协议里的api已经够好了,拿来改进一下就可以了.另外Appium可以把server放在任意机器上,哪怕是云服务器都可以,所以Appium和We

nagios简介与原理

1.Nagios简介 1.与cacti的区别 a) Cacti 1.Cacti比较着重于直观数据的监控,易于生成图形,用来监控网络流量.cpu使用率.硬盘使用率等可以说很在合适不过 2.通过SNMP监控数据 3.展示工具 4.用插件来增加模块做监控 b) nagios 1.比较注重于主机和服务的监控,并且有很强大的发送报警信息的功能 2.监控方式更多 3.配置灵活的监控工具 4.脚本和agent做监控 2.监控对象 a) 主机.主机组:服务器.虚拟机.网络设备 b) 服务/资源,服务组:http

tomcat简介及原理解说

Tomcat简介 作者:杨晓(http://blog.sina.com.cn/u/1237288325) 目录: ----Tomcat背景 ----Tomcat目录 ----Tomcat类加载 ----server.xml配置简介 ----web.xml配置简介 ----管理 ----tomcat原理解说 ----Tomcat Server的组成部分 ----Tomcat Server的结构图 ----配置文件$CATALINA_HOME/conf/server.xml的说明 ---Contex

rpc简介、原理、实例

简介 RPC(Remote Procedure Call,远程过程调用)是建立在Socket之上的,出于一种类比的愿望,在一台机器上运行的主程序,可以调用另一台机器上准备好的子程序,就像LPC(本地过程调用).越底层,代码越复杂.灵活性越高.效率越高:越上层,抽象封装的越好.代码越简单.效率越差.Socket和RPC的区别再次说明了这点.在传统的编程概念中,过程是由程序员在本地编译完成,并只能局限在本地运行的一段代码,也即其主程序和过程之间的运行关系是本地调用关系.因此这种结构在网络日益发展的今

【原创】源码角度分析Android的消息机制系列(三)——ThreadLocal的工作原理

ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 先看Android源码(API24)中对ThreadLocal的定义: public class ThreadLocal<T> 即ThreadLoca是一个泛型类,再看对该类的注释: /** * This class provides thread-local variables. These variables differ from * their normal counterparts in that each thread th

ThreadLocal使用以及原理

介绍 ThreadLocal是一个用于创建线程局部变量的类.当前线程通过ThreadLocal的set()方法设置的变量只对当前线程可见,通过get()获取设置的变量. 使用 支持泛型 ThreadLocal<String> threadLocal = new ThreadLocal<>(); 当前线程通过ThreadLocal对象的set(value)/get()设置变量和获取设置的变量 threadLocal.set("jinshuai"); threadL

ThreadLocal使用和原理简析

1. 解决共享资源冲突 对于并发工作,需要某种方式来防止两个任务同时访问相同的资源,至少在关键阶段不能出现这种冲突情况. 方法之一就是当资源被一个任务使用时,在其上加锁.第一个访问某项资源的任务必须锁定这项资源,使其他任务在其被解锁之前,就无法访问它了,而在其被解锁之时,另一个任务就可以锁定并使用它,以此类推.Java中的synchronized.ReentrantLock就属于这种方式,关于这部分,前面有专门撰文详述: synchronized学习.synchronized底层实现学习 Ree