标签:回收 数据库连接 场景 loading 不同 代码 初始化 weakref private
多线程在访问同一个共享变量操作时,尤其是写入操作时,为了保证线程安全,都会做一些额外的同步措施。ThreadLocal提供了另一种不靠加锁就能保证线程安全的方式。ThreadLocal是JDK包提供的,它提供线程本地变量,如果创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,在实际多线程操作的时候,操作的都是自己本地内存中的变量,从而规避线程安全问题。
ThreadLocal有一个内部静态类ThreadLocalMap,然后在Thread类中,有两个成员变量:threadLocals 和 inheritableThreadLocals,这两个都是ThreadLocalMap类型的。
这两个变量的默认值都是null,只有在线程第一次调用threadlocal的set或者get方法时,才会创建它们。每个线程的变量副本并不是存放在threadlocal实例中,而是存放在thread类中的threadlocals中,当取值的时候,用get方法从threadlocals中获取变量;
public void set(T value) { //(1)获取当前线程(调用者线程) Thread t = Thread.currentThread(); //(2)以当前线程作为key值,去查找对应的线程变量,找到对应的map ThreadLocalMap map = getMap(t); //(3)如果map不为null,就直接添加本地变量,key为当前线程,值为添加的本地变量值 if (map != null)
// this 是指的在程序中创建的threadlocal变量,比如:在程序中创建了多个threadlocal变量
// ThreadLocal<String> a = new ThreadLocal<>(); ThreadLocal<Integer> b = new ThreadLocal<>();
// 这个this就是指代a、b;
map.set(this, value);
//(4)如果map为null,说明首次添加,需要首先创建出对应的map else createMap(t, value); }
ThreadLocalMap getMap(Thread t) { return t.threadLocals; //获取线程自己的变量threadLocals,并绑定到当前调用线程的成员变量threadLocals上 }
2. get方法解析
public T get() { //(1)获取当前线程 Thread t = Thread.currentThread(); //(2)获取当前线程的threadLocals变量 ThreadLocalMap map = getMap(t); //(3)如果threadLocals变量不为null,就可以在map中查找到本地变量的值 if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } //(4)执行到此处,threadLocals为null,调用该更改初始化当前线程的threadLocals变量 return setInitialValue(); } private T setInitialValue() { //protected T initialValue() {return null;} T value = initialValue(); //获取当前线程 Thread t = Thread.currentThread(); //以当前线程作为key值,去查找对应的线程变量,找到对应的map ThreadLocalMap map = getMap(t); //如果map不为null,就直接添加本地变量,key为当前线程,值为添加的本地变量值 if (map != null) map.set(this, value); //如果map为null,说明首次添加,需要首先创建出对应的map else createMap(t, value); return value; }
3. remove方法解析
public void remove() { //获取当前线程绑定的threadLocals ThreadLocalMap m = getMap(Thread.currentThread()); //如果map不为null,就移除当前线程中指定ThreadLocal实例的本地变量 if (m != null) m.remove(this); }
ThreadLocal的数据其实都是存放在ThreadLocalMap中,而ThreadLocalMap内部实际上是一个Entry数组,这个Entry的内部类如下:
/** * 是继承自WeakReference的一个类,该类中实际存放的key是 * 指向ThreadLocal的弱引用和与之对应的value值(该value值 * 就是通过ThreadLocal的set方法传递过来的值) * 由于是弱引用,当get方法返回null的时候意味着坑能引用 */ static class Entry extends WeakReference<ThreadLocal<?>> { /** value就是和ThreadLocal绑定的 */ Object value; //k:ThreadLocal的引用,被传递给WeakReference的构造方法 Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } //WeakReference构造方法(public class WeakReference<T> extends Reference<T> ) public WeakReference(T referent) { super(referent); //referent:ThreadLocal的引用 } //Reference构造方法 Reference(T referent) { this(referent, null);//referent:ThreadLocal的引用 } Reference(T referent, ReferenceQueue<? super T> queue) { this.referent = referent; this.queue = (queue == null) ? ReferenceQueue.NULL : queue; }
从代码中可以看出,threadLocal的引用被传递到weakReference的构造函数中【super(key)】,所以threadlocalMap中的key是一个threadlocal的弱引用,而弱引用在gc时会被直接回收掉,就有可能造成ThreadLocaMap中的key为null,而value不是null。这样的话,如果线程一直存在,那么threadLocaMap不会被回收,所以里面存储的threadlocal的entry就会越来越多,直到内存溢出。所以,每次使用完毕之后,需要显示的调用remove方法,删除threadlocalmap。
session管理,区分不同线程的session信息;
数据库连接;
标签:回收 数据库连接 场景 loading 不同 代码 初始化 weakref private
原文地址:https://www.cnblogs.com/anduodefeng/p/13151616.html