其他分享
首页 > 其他分享> > 浅谈threadlocal

浅谈threadlocal

作者:互联网

1.什么是threadlocal?

ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

  2.应用场景? 1、在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。 2、线程间数据隔离 3、进行事务操作,用于存储线程事务信息。 4、数据库连接,Session会话管理。       3.源码解析(set,get,romeve三个方法)

 1、set方法源码

复制代码
 1 public void set(T value) {
 2     //(1)获取当前线程(调用者线程)
 3     Thread t = Thread.currentThread();
 4     //(2)以当前线程作为key值,去查找对应的线程变量,找到对应的map
 5     ThreadLocalMap map = getMap(t);
 6     //(3)如果map不为null,就直接添加本地变量,key为当前定义的ThreadLocal变量的this引用,值为添加的本地变量值
 7     if (map != null)
 8         map.set(this, value);
 9     //(4)如果map为null,说明首次添加,需要首先创建出对应的map
10     else
11         createMap(t, value);
12 }

 在上面的代码中,(2)处调用getMap方法获得当前线程对应的threadLocals(参照上面的图示和文字说明),该方法代码如下

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals; //获取线程自己的变量threadLocals,并绑定到当前调用线程的成员变量threadLocals上
}

如果调用getMap方法返回值不为null,就直接将value值设置到threadLocals中(key为当前线程引用,值为本地变量);如果getMap方法返回null说明是第一次调用set方法(前面说到过,threadLocals默认值为null,只有调用set方法的时候才会创建map),这个时候就需要调用createMap方法创建threadLocals,该方法如下所示

1 void createMap(Thread t, T firstValue) {
2     t.threadLocals = new ThreadLocalMap(this, firstValue);
3 }

  createMap方法不仅创建了threadLocals,同时也将要添加的本地变量值添加到了threadLocals中。

 

2、get方法源码

  在get方法的实现中,首先获取当前调用者线程,如果当前线程的threadLocals不为null,就直接返回当前线程绑定的本地变量值,否则执行setInitialValue方法初始化threadLocals变量。在setInitialValue方法中,类似于set方法的实现,都是判断当前线程的threadLocals变量是否为null,是则添加本地变量(这个时候由于是初始化,所以添加的值为null),否则创建threadLocals变量,同样添加的值为null。

复制代码
 1 public T get() {
 2     //(1)获取当前线程
 3     Thread t = Thread.currentThread();
 4     //(2)获取当前线程的threadLocals变量
 5     ThreadLocalMap map = getMap(t);
 6     //(3)如果threadLocals变量不为null,就可以在map中查找到本地变量的值
 7     if (map != null) {
 8         ThreadLocalMap.Entry e = map.getEntry(this);
 9         if (e != null) {
10             @SuppressWarnings("unchecked")
11             T result = (T)e.value;
12             return result;
13         }
14     }
15     //(4)执行到此处,threadLocals为null,调用该更改初始化当前线程的threadLocals变量
16     return setInitialValue();
17 }
18 
19 private T setInitialValue() {
20     //protected T initialValue() {return null;}
21     T value = initialValue();
22     //获取当前线程
23     Thread t = Thread.currentThread();
24     //以当前线程作为key值,去查找对应的线程变量,找到对应的map
25     ThreadLocalMap map = getMap(t);
26     //如果map不为null,就直接添加本地变量,key为当前线程,值为添加的本地变量值
27     if (map != null)
28         map.set(this, value);
29     //如果map为null,说明首次添加,需要首先创建出对应的map
30     else
31         createMap(t, value);
32     return value;
33 }
复制代码   3、remove方法的实现

  remove方法判断该当前线程对应的threadLocals变量是否为null,不为null就直接删除当前线程中指定的threadLocals变量

复制代码
1  public void remove() {
2     //获取当前线程绑定的threadLocals
3      ThreadLocalMap m = getMap(Thread.currentThread());
4      //如果map不为null,就移除当前线程中指定ThreadLocal实例的本地变量
5      if (m != null)
6          m.remove(this);
7  }
复制代码     4.threadlocal源码分析总结: (1)每个Thread维护着一个ThreadLocalMap的引用 (2)ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储 (3)ThreadLocal创建的副本是存储在自己的threadLocals中的,也就是自己的ThreadLocalMap。 (4)ThreadLocalMap的键值为ThreadLocal对象,而且可以有多个threadLocal变量,因此保存在map中 (5)在进行get之前,必须先set,否则会报空指针异常,当然也可以初始化一个,但是必须重写initialValue()方法。 (6)ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。     5.原理: 首先,在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。     6.注意: 重点来了,突然我们ThreadLocal是null了,也就是要被垃圾回收器回收了,但是此时我们的ThreadLocalMap生命周期和Thread的一样,它不会回收,这时候就出现了一个现象。那就是ThreadLocalMap的key没了,但是value还在,这就造成了内存泄漏。 解决办法:使用完ThreadLocal后,执行remove操作,避免出现内存溢出情况。 ThreadLocal不支持继承性     ThreadLocal类是不能提供子线程访问父线程的本地变量的,而InheritableThreadLocal类则可以做到     题外基础补充:

①强引用:Java中默认的引用类型,一个对象如果具有强引用那么只要这种引用还存在就不会被GC。

②软引用:简言之,如果一个对象具有弱引用,在JVM发生OOM之前(即内存充足够使用),是不会GC这个对象的;只有到JVM内存不足的时候才会GC掉这个对象。软引用和一个引用队列联合使用,如果软引用所引用的对象被回收之后,该引用就会加入到与之关联的引用队列中

③弱引用(这里讨论ThreadLocalMap中的Entry类的重点):如果一个对象只具有弱引用,那么这个对象就会被垃圾回收器GC掉(被弱引用所引用的对象只能生存到下一次GC之前,当发生GC时候,无论当前内存是否足够,弱引用所引用的对象都会被回收掉)。弱引用也是和一个引用队列联合使用,如果弱引用的对象被垃圾回收期回收掉,JVM会将这个引用加入到与之关联的引用队列中。若引用的对象可以通过弱引用的get方法得到,当引用的对象呗回收掉之后,再调用get方法就会返回null

④虚引用:虚引用是所有引用中最弱的一种引用,其存在就是为了将关联虚引用的对象在被GC掉之后收到一个通知。(不能通过get方法获得其指向的对象)

                   



标签:map,浅谈,threadlocal,threadLocals,引用,线程,null,变量
来源: https://www.cnblogs.com/zuoyi2319516228/p/15597438.html