首页 > 编程知识 正文

threadlocal使用场景和原理,threadlocal工作原理

时间:2023-05-06 07:12:20 阅读:263224 作者:440

1、ThreadLocal变量

线程局部变量,是每一个线程所单独持有的,其他线程不能对其进行访问。
当使用ThreadLocal维护变量的时候,为每一个使用该变量的线程提供一个独立的变量副本,即每个线程内部都会有一个该变量,这样同时多个线程访问该变量并不会彼此相互影响,因此他们使用的都是自己从内存中拷贝过来的变量的副本, 这样就不存在线程安全问题,也不会影响程序的执行性能。

2、使用场景 每个线程需要有自己单独的实例实例需要在多个方法中共享,但不希望被多线程共享

例如存储用户Session
而线程同步正好相反,线程同步机制都是为了解决多线程中相同变量的访问冲突问题。线程同步机制需要保证多个线程都能准确的获取到共享变量的实时值,而ThreadLocal是只关心自己线程的副本,不关心其他线程。每个线程有自己独立的副本,自然也不存在线程安全问题。

3、实现原理

ThreadLocal中有一个静态内部类ThreadLocalMap(一个简化版的HashMap),用来存储每个线程独有的ThreadLocal变量,以ThreadLocal对象的弱引用为key,ThreadLocal值为value。

Thread对象中有一个成员ThreadLocal.ThreadLocalMap threadLocals,用来存储当前线程的所有ThreadLocal变量,对ThreadLocal每次进行set、get、remove等操作都是对Thread的该成员进行操作,从而做到线程隔离。
ThreadLocal对象的set方法实现:

public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } ThreadLocalMap getMap(Thread t) { return t.threadLocals; } void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }

先获取当前线程对象t,获取它的threadLocals成员,若存在直接对map进行set,不存在则新建一个ThreadLocalMap对象赋给threadLocals。

4、问题

内存泄露问题
实际上 ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,弱引用的特点是,如果这个对象只存在弱引用,那么在下一次垃圾回收的时候必然会被清理掉。
如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候会被清理掉的,这样一来 ThreadLocalMap中使用这个 ThreadLocal 的 key 也会被清理掉。但是,value 是强引用,不会被清理,这样一来就会出现 key 为 null 的 value。
ThreadLocalMap实现中已经考虑了这种情况,在调用 set()、get()、remove() 方法的时候,会清理掉 key 为 null 的记录。如果说会出现内存泄漏,那只有在出现了 key 为 null 的记录后,没有手动调用 remove() 方法,并且之后也不再调用 get()、set()、remove() 方法的情况下。
例如,我们会把当前请求的request对象放到ThreadLocal中,后续处理流程中可以方便的获取request对象,而不用一层一层的往下传递request对象。但是使用完成之后一定要remove.
脏数据问题
由于项目基本上都是使用线程池,因为Thread对象是复用的,那么与Thread对象绑定的ThreadLocal变量也可能被复用。
父子线程共享线程变量
很多场景下通过ThreadLocal来透传全局上下文,会发现子线程的value和主线程不一致。比如用ThreadLocal来存储监控系统的某个标记位,暂且命名为traceId。某次请求下所有的traceld都是一致的,以获得可以统一解析的日志文件。但在实际开发过程中,发现子线程里的traceld为null,跟主线程的并不一致。

参考1
参考2

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。