Java中ThreadLocal线程变量的实现原理

目录

  • ThreadLocal是什么?
  • ThreadLocal实现原理分析
  • ThreadLocal内存泄漏问题

ThreadLocal是什么? ThreadLocal 使得我们可以创建线程私有的变量, 这个变量相对于其他线程来说是不可见的,ThreadLocal为变量在每个线程中都创建了一个副本 , 每个线程可以访问自己私有的线程变量,代码示例如下 :
public class ThreadLocalDemo { //创建一个ThreadLocal对象,用来为每个线程会复制保存一份变量,实现线程封闭privatestatic ThreadLocal localNum = new ThreadLocal(){@Overrideprotected Integer initialValue() {return 1; }}; public static void main(String[] args) {//线程0new Thread(){@Overridepublic void run() {localNum.set(1); try {Thread.sleep(2000); } catch (InterruptedException e) {e.printStackTrace(); }localNum.set(localNum.get()+10); System.out.println(Thread.currentThread().getName()+":"+localNum.get()); //11}}.start(); //线程1new Thread(){@Overridepublic void run() {localNum.set(3); try {Thread.sleep(2000); } catch (InterruptedException e) {e.printStackTrace(); }localNum.set(localNum.get()+20); System.out.println(Thread.currentThread().getName()+":"+localNum.get()); //23}}.start(); System.out.println(Thread.currentThread().getName()+":"+localNum.get()); //0}}

如上所述, 算上main线程与新建的两个线程 ,总共三个线程 , 每个线程都包含自己的私有变量,此处我们设置值1 , set() 和 get() 方法用来设置值和获得值, 执行结果如下 :
Java中ThreadLocal线程变量的实现原理
文章图片


ThreadLocal实现原理分析 ThreadLocal是一个泛型类 , 可以接受任何类型的对象 , 其内部维护了一个ThreadLocalMap 的静态内部类,我们使用的 get(), set()等其实都来自这个类, 每次都会为当前线程创建一个ThreadLocalMap对象, 用来记录私有的值
Java中ThreadLocal线程变量的实现原理
文章图片

先看 set() 方法
public void set(T value) {//拿到当前线程Thread t = Thread.currentThread(); //拿到当前线程mapThreadLocalMap map = getMap(t); if (map != null)//存在设置值map.set(this, value); else//不存在则创建createMap(t, value); }

void createMap(Thread t, T firstValue) {//threadLocals属性即为此mapt.threadLocals = new ThreadLocalMap(this, firstValue); }

接着是get() 方法
public T get() {//拿到当前线程Thread t = Thread.currentThread(); //拿到当前线程对应的mapThreadLocalMap map = getMap(t); //如果已有mapif (map != null) {//取值操作, 拿到对应的EntryThreadLocalMap.Entry e = map.getEntry(this); if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value; return result; }}//没有map, 则去创建初始化一个mapreturn setInitialValue(); }

private T setInitialValue() {//initialValue()方法返回的value为nullT value = https://www.it610.com/article/initialValue(); //拿到当前线程去创建对应的mapThread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null)map.set(this, value); elsecreateMap(t, value); return value; }

ThreadLocal可以理解为对ThreadLocalMap的封装

ThreadLocal内存泄漏问题 在ThreadLocalMap中 , 使用 ThreadLocal 的弱引用作为 key
Java中ThreadLocal线程变量的实现原理
文章图片

这样的话, 如果一个ThreadLocal不存在外部强引用时, 那么key注定要被GC回收 , 这样导致ThreadLocalMap 中key为null , 而value还存在着强引用链
【Java中ThreadLocal线程变量的实现原理】一个线程可以同时拥有多个ThreadLocal, 如果作为弱引用的key被回收后, value还不能被回收,那么这就导致此ThreadLocal的生命周期和此线程是一样长的(因为线程执行完毕后此value的强引用链才会断), 如果线程一直不结束, 堆积的value也一直无法被回收, 那么就会产生内存泄漏问题
这里解决问题的方式是 : 每次使用完ThreadLocal后都调用它的remove()方法清除数据
public void remove() {ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null)m.remove(this); }

这里我们再来看一下key作为强弱引用的区别
如果key作为强引用, 那么它的生命周期和线程一样长,存在稳定的强引用链,无法被回收,产生内存泄漏问题, 而如果作为弱引用, GC则会自动的去回收它们, 在后续的remove()方法中也可以更好的去回收value , 所以我们一般将ThreadLocal设计成 private static 的, 在使用完后用remove()方法去手动删除它们
到此这篇关于Java中ThreadLocal线程变量的实现的文章就介绍到这了,更多相关Java ThreadLocal线程变量内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    推荐阅读