ThreadLocal

1,ThreadLocal是什么:

ThreadLocal也是解决多线程访问共享变量问题的,不同于加锁,他会在每一个线程内部创建副本,

每个线程独立访问自己内部的副本,不会产生冲突。

 

2,用法:

示例:

  public class ThreadLocalTest {
  
      static ThreadLocal<String> localVar = new ThreadLocal<>();
  
      static void print(String str) {
          //打印当前线程中本地内存中本地变量的值
          System.out.println(str + " :" + localVar.get());
         //清除本地内存中的本地变量
         localVar.remove();
     }
 
     public static void main(String[] args) {
         Thread t1  = new Thread(new Runnable() {
            @Override
             public void run() {
                 //设置线程1中本地变量的值
                 localVar.set("localVar1");
                 //调用打印方法
                 print("thread1");
                 //打印本地变量
                 System.out.println("after remove : " + localVar.get());
             }
         });
 
         Thread t2  = new Thread(new Runnable() {
             @Override
             public void run() {
                 //设置线程1中本地变量的值
                 localVar.set("localVar2");
                 //调用打印方法
                 print("thread2");
                 //打印本地变量
                 System.out.println("after remove : " + localVar.get());
             }
         });
 
        t1.start();
         t2.start();
     }
}
View Code

3,原理:

ThreadLocal类:

①内部类:ThreadLocalMap

是一个哈希表,循环数组+开发地址法中线性探测法解决哈希冲突。

什么意思呢?试试想一下:线性探测法会在数组中形成以null隔开的若干‘块’,我们把它类比成拉链法里面的每一个链表。

线性探测法在一个快中(快可能未null)添加元素就相当于尾插法,删除元素,就要调整该快中,被删除元素之后的元素,要前移

保证他们下次能被探测到。就绪拉链法里面的调整指针关系。

Entry数组储存的是key-value键值对,key是ThreadLocal,value是Object对象。

  • 参数:

  • 方法:主要是哈希表的,增删改查

①增删改查之前,首先要介绍一下expungeStaleEntry()函数,顾名思义删除元素用的

②getEntry():

getEntryAfterMiss():

③set()函数:

replaceStaleEntry()

 

cleanSomeSlots();

 ④remove()

 ⑤rehash()

 expungeStaleEntries()

 

 resize()扩容:

总结:ThreadLocalMap(),哈希表,数组+线性探测法解决哈希冲突。初始容量16,扩容*2.

在增删改查中不停清理,失效的TreadLocal元素。

内存泄漏问题:

<1>ThreadLocalMap的Entry数组是key为ThreadLocal的弱引用,而valve是强引用。如果ThreadLocal对象没有被外部强引用,垃圾回收时,key被回收

为null但是value还在。造成内存泄漏。

<2>虽然ThreadLocalMap在get(),set()函数里多次清理key为null的失效元素,但是当没有调用get(),set()时,不会清理。

 

解决方法:<1>使用完ThreadLocal后手动调用remove方法。

<2>将ThreadLocal定义为private static 随线程一起消亡。

 

②看完ThreadLocalMap,在回到ThreadLocal类,

  • set()

 

  • get()

 map空就初始化map

  • 虽然ThreadLocalMap是ThreadLocal内部类,但ThreadLocal里并没有ThreadLocalMap参数,

而是通过getMap()方法;获得线程的threadlocalS。

                               调用getMap的地方也是传入当前线程的

 

                进入Thread类:

ThreadLocalMap实例化在Thread类里作为参数。即每一个线程有一个,哈西表,threadlocals,顾名思义用来存放该

线程ThreadLocals变量的。

ThreadLocal是ThreadLocalMap的封装,提供了set(),get();看起来是每个Threadlocal变量单独get(),set()。背后还是通过每个Thred的

ThredLocalMap统一管理所有ThreadLocal变量;

 参考:

https://www.cnblogs.com/fsmly/p/11020641.html

https://baijiahao.baidu.com/s?id=1653790035315010634&wfr=spider&for=pc

https://www.jianshu.com/p/80866ca6c424

https://blog.csdn.net/ywlmsm1224811/article/details/91388109

原文地址:https://www.cnblogs.com/wangpan8721/p/13800599.html