ThreadLocal线程局部变量

1.原理

ThreadLocal是用来保存当前 线程数据的,每一个线程的内部都有一个ThreadLocalMap,当前这个map中存储了以当前ThreadLocal作 ,具体的数据作的一个个Entry对象。

为什么非得以ThreadLocal对象作键呢?因为一个线程可能使用了不止一个ThreadLocal对象,如果以当前线程对象做键,再去找对应的ThreadLocal就很麻烦,产生混淆。

由于每一个线程都有属于自己的ThreadLocal线程局部变量,所以很好的实现了线程之间的数据隔离(ThreadLocal中保存的数据仅属于当前线程)。

2.ThreadLocal中的常见方法:

(1)存储数据 set()

(2)获取数据 get()

(3)清除数据 remove()

那么问题来了,具体是怎样利用ThreadLocalMap查找数据的呢?不论是set()、get()、remove()等方法对当前map进行操作时,最终都定位到了通过计算出的下标来操作。

而这个下标是通过++哈希算法++ 计算得到的,操作ThreadLocalMap是以当前的ThreadLocal作key,通过++当前的key的HashCode值++ 和Entry[]++数组长度-1++ 作**"&"**运算,来计算出实际操作位置的下标,从而达到访问元素的目的。

作按位与运算的效果和利用哈希值直接进行取余%运算一样,但效率大大提高

3.如何实现父子线程共享数据?

利用了jdk提供ThreadLocal的子类++InheritableThreadLocal++来实现。

java 复制代码
public class ThreadLocalTest {

    public static void main(String[] args) {
    	InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<String>();
		threadLocal.set("风萧萧兮易水寒");
		System.out.println("main主线程:"+threadLocal.get());
		
		Thread thread = new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println("子线程:"+threadLocal.get());
			}
		});
		thread.start();
    }
}

main函数为父线程,创建了一个thread子线程,利用InheritableThreadLocal这个子类来共享父线程的数据。

父线程子线程均输出"风萧萧兮易水寒"。

4.ThreadLocal如何避免内存泄露

执行完相关的业务逻辑后,最终在finally代码块中都会调用++remove()++ 方法,将当前map中的ThreadLocal键置为空,value置为空,从而在垃圾回收的时候及时回收无用数据。

5.应用

(1) 线程的数据隔离

因为ThreadLocal对象只属于当前线程,那么ThreadLocal中的数据也属于当前线程,在多线程并发的情况下,很好的实现了不同线程的数据隔离,避免了采用synchronized锁机制来保证线程安全而导致的性能上的代价。

例如:SqlSession会话对象绑定,避免多个线程使用同一个SqlSession对象,由于关闭导致异常。

java 复制代码
//当前线程的线程局部变量
private static final ThreadLocal threadSession= new ThreadLocal();

public static SqlSession getSession(){//获取session会话方法
    SqlSession s = (SqlSession)threadSession.get();//通过仅属于当前线程的threadSession对象来获取
    if(s==null){//为空
        s = getSqlSessionFactory().openSqlSession();//则重新建立会话
        threadSession.set(s);//并存到ThreadLocalMap中去
    }
}

(2)跨函数调用

数据通常用于同一个类中的传递,如果利用方法传递势必要关心方法的返回值类型及参数,但利用ThreadLocal可以直接实现获取,这样做还达到了解耦的效果。

例如:RequestContextHolder源码就有很好的体现

java 复制代码
@Nullable  
public static RequestAttributes getRequestAttributes() {
    // 获取当前线程中的存储的Request Attribute
    //直接通过ThreadLocal对象来获取
    RequestAttributes attributes = requestAttributesHolder.get();  
    if (attributes == null) {  
        attributes = inheritableRequestAttributesHolder.get();  
    }  
    return attributes;  
}

private static final ThreadLocal<RequestAttributes> requestAttributesHolder =  new NamedThreadLocal<>("Request attributes");  
  
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =  new NamedInheritableThreadLocal<>("Request context");

以上就是我对ThreadLocal线程区域对象的粗略理解,欢迎诸君共同探讨。

相关推荐
少废话h17 分钟前
解决Flink中ApacheCommonsCLI版本冲突
开发语言·python·pycharm
天命码喽c20 分钟前
GraphRAG-2.7.0整合Milvus-2.5.1
开发语言·python·milvus·graphrag
后端小张22 分钟前
【JAVA进阶】Spring Boot 核心知识点之自动配置:原理与实战
java·开发语言·spring boot·后端·spring·spring cloud·自动配置
tg-zm8899965 小时前
2025返利商城源码/挂机自动收益可二开多语言/自定义返利比例/三级分销理财商城
java·mysql·php·laravel·1024程序员节
X***C8625 小时前
SpringBoot:几种常用的接口日期格式化方法
java·spring boot·后端
Mr_Xuhhh5 小时前
YAML相关
开发语言·python
咖啡の猫6 小时前
Python中的变量与数据类型
开发语言·python
前端达人6 小时前
你的App消息推送为什么石沉大海?看Service Worker源码我终于懂了
java·开发语言
汤姆yu6 小时前
基于springboot的电子政务服务管理系统
开发语言·python
小光学长6 小时前
基于ssm的宠物交易系统的设计与实现850mb48h(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
java·前端·数据库