ThreadLocal(个人笔记)

ThreadLocal的作用:提供线程内的局部变量,不同的线程之间不会相互干扰,这种变量在线程的生命周期起作用,减少同一个线程内多个函数或组件之间一些公共变量传递的复杂度。
总结:
1、线程并发:在多线程并发的情景下
2、传递数据:我们可以通过ThreadLocal在同一个线程,不同组件中传递公共变量
3、线程隔离:每个线程的变量都是独立的,不会相互影响
1.3
ThreadLocal和Synchronized
相同点:都是用于处理多线程并发访问变量的问题。
Synchronized:同步机制采用以时间换空间的方式,只提供一份变量,让不同的线程排队访问,侧重多个线程之间访问资源的同步,失去了多线程并发
ThreadLocal:采用以空间换时间,为每个线程都提供了一份变量的副本,从而实现同时访问而互不干扰,侧重多线程中让每个线程之间的数据相互隔离。可以使程序拥有一个更高的并发性。

2、运用场景_事务案例

2.1转账案例
ThreadLocal方案的好处:
1、传递数据:保证每个线程绑定的数据,在需要的地方直接 获取,避免参数直接传递带来的代码耦合问题。
2、线程隔离:各线程之间的数据相互隔离却又具备并发性,避免同步方式带来的性能损失。
2.2现在的设计
jdk8中ThreadLocal的设计是:每个Thread维护一个TreadLocalMap。这个Map的key是ThreadLocal实例本身,value才是真正要存储的值object。
jdk8的设计方案好处:
**1、**每个Map存储的Entry数量变少
**2、**当Thread销毁的时候,ThreadLocal也会随之销毁,减少内存的使用
2.3ThreadLocal的核心方法源码
|方法声明 | 描述 |
protected T initalValue() 返回当前线程局部变量的初始值
public void set (T value) 设置当前线程绑定的局部变量
public T get() 获取当前线程绑定的局部变量
public void remove() 移除当前线程绑定的局部变量

弱引用和内存泄漏

内存泄漏:程序中已分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。内存泄漏的堆积最终会导致内存溢出。

强引用:就是我们最常见的普通对象的引用,只要还有强引用指向一个对象,就能表明对象还活着,垃圾回收器就不会回收这种对象。
弱引用:垃圾回收器一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

不管ThreadLocalMap中的key使用弱引用还是强引用,都会出现内存泄漏。在没有手动删除Entry以及CurrentThread依然运行的情况下,也存在强引用链threadRef—>currentThread—>threadLocalMap—>entry—>value,value不会被回收,而这块vlaue永远也不会被访问到了,导致value内存泄漏。

出现内存泄漏的真实原因:
1、没有手动删除这个Entry
2、CurrentThread依然运行
为什么使用弱引用?
在使用完ThreadLocal,CurrentThread依然运行的前提下,就算忘记调用remove方法,弱引用比强引用可以多一层保障。弱引用的ThreadLocal会被回收,对应的value在下一次ThreadLocalMap调用set、get、remove方法的任一方法的时候就会被清除,从而避免内存泄漏。

hash冲突解决

ThreadLocalMap使用线程探测法来解决哈希冲突的。该方法一次探测下一个地址,直到有空的地址后插入,若整个空间都找不到空余的地址,则产生溢出。我们可以把ThreadLocalMap底层的那个Entry[]数组 table看成一个环形数组。

你用过 ThreadLocal 吗? 它的实现原理是什么? 虽然问题简洁明了直入主题,但是答案中却暗藏了很多坑。

我的答案

ThreadLocal 是一个可以提供线程本地变量的工具类,使用它声明的变量在每个线程访问的时候会单独的初始化变量的一个线程副本,这个副本与当前线程会关联起来,存储在线程本地达到与其他线程隔离的目的,从而提供线程本地变量的特性。 这就是 ThreadLocal 类,想必每个 Java 程序员都应该知道这一点,所以在此就不多啰嗦了。 说到 ThreadLocal,我们主要注意如下几个点:

它具体怎么存储的

ThreadLocal 会被存储在一个 ThreadLocalMap(ThreadLocal 的静态内部类) 的容器里面,当线程第一次访问一个 ThreadLocal 类型的变量的时候会初始化一个 ThreadLocalMap 实例作为当前线程的属性。 然后将当前 ThreadLocal 作为 Key,值作为 Value 存储这个 map 当中。 当该线程后续访问其他的 ThreadLocal 变量的时候就不需要重新初始化 map 了,所有 ThreadLocal 变量都会被存储在其中。

ThreadLocalMap 与 HashMap 有什么不同之处

说到 map 难免要和 HashMap 来对比一下,首先 ThreadLocalMap 专门是用来存储 ThreadLocal 的,所以设计比较简单。

元素包装

同样它内部存储是依赖于一个静态内部类 Entry 的数组,这个 Entry 的特殊之处在于它是一个 WeakReference,想必大家都知道 Java 中的四种引入类型: 强,软,弱,虚。 其中弱引用会被在垃圾回收时候直接回收掉,这也是为内存的垃圾回收埋下了伏笔。 当 ThradLocal 的变量不会再被使用到的时候,下一次垃圾回收即可回收掉 ThreadLocalMap 中的 Entry 对象,而假设 Entry 不是 WeakReference,则只有等到线程销毁才会被回收掉,这样会导致一些垃圾数据长时间占用 JVM 的内存。

hash 冲突

我们都知道 HashMap 在发生冲突的时候会采用拉链法,将冲突的元素链式存储在同一个槽里面。 而ThreadLocalMap 采取了另外一种方式,如果当前槽中已经有元素,那么它试图存入后一个槽中,直到找到可以容纳自己的槽。

会造成内存溢出吗

关于这一点,网上看到一大堆文章分析说 ThreadLocal 会发生内存泄漏问题,因为虽然被 WeakReference 的 Entry 被回收掉了,但是 Value 还在 ThreadLocalMap 中,只要线程一直存活着(例如线程池技术)就会始终保留着 Value。 没错确实是这样,但是只要该线程再次操作 ThreadLocal 类型的变量,就会触发清理掉无用的 ThreadLocal 变量,这样就达到了垃圾清理的目的。 所以我认为根本不需要考虑这种这种问题,除非你的 ThreadLocal 变量非常大,随便实例化几个就会把内存撑爆,如果真是这种情况,那么你需要考虑一下你的系统设计的合理性了。

原文地址:https://www.cnblogs.com/kiki-study/p/13656119.html