鲅鱼圈网站在哪做,沪上装修排名前十有哪些品牌,eclipse做网站,手机模板网站开发关键点总结#xff1a; ThreadLocal更像是对其他类型变量的一层包装#xff0c;通过ThreadLocal的包装使得该变量可以在线程之间隔离和当前线程全局共享。在Thread中有一个threadLocals变量#xff0c;类型为ThreadLocal.ThreadLocalMap#xff0c;ThreadLocalMap中key是Th… 关键点总结 ThreadLocal更像是对其他类型变量的一层包装通过ThreadLocal的包装使得该变量可以在线程之间隔离和当前线程全局共享。在Thread中有一个threadLocals变量类型为ThreadLocal.ThreadLocalMapThreadLocalMap中key是ThreadLocal,value是存入的变量值。Thread中threadLocals由ThreadLocal维护。每个线程的本地变量不是存储在ThreadLocal示例里边的而是存放在调用线程的threadLocals变量里边。LoreadLocal类型的本地变量存放在具体的线程内存空间中。ThreadLocal就是一个工具通过set方法把value值放入调用线程的threadLocals变量。调用**get**方法再从当前线程的threadLocals中拿出数据。如果调用线程一直不终止那么这个本地变量会一直存放在调用线程的thradLocals变量里边所以当不需要使用本地变量的时候可以通过调用ThreadLocal的remove方法删除本地变量Java中的内存泄露的情况长生命周期的对象持有短生命周期的对象的引用就很有可能发生内存泄露尽管短生命周期对象已经不再需要但是因为长生命周期对象持有它的引用而导致不能被回收这就是Java中内存泄露发生的场景。 概述 ThreadLocal线程本地变量用于解决多线程并发访问时共享变量的问题。** ThreadLocal实现原理
存储原理 在Thread中有一个threadLocals变量类型为ThreadLocal.ThreadLocalMapThreadLocalMap中key是ThreadLocal,value是存入的变量值。Thread中threadLocals由ThreadLocal维护。每个线程的本地变量不是存储在ThreadLocal示例里边的而是存放在调用线程的threadLocals变量里边。LoreadLocal类型的本地变量存放在具体的线程内存空间中。ThreadLocal就是一个工具通过set方法把value值放入调用线程的threadLocals变量。调用**get**方法再从当前线程的threadLocals中拿出数据。如果调用线程一直不终止那么这个本地变量会一直存放在调用线程的thradLocals变量里边所以当不需要使用本地变量的时候可以通过调用ThreadLocal的remove方法删除本地变量 ThreadLocal常用方法
set(T value)设置线程本地变量的内容。get()获取线程本地变量的内容。remove()移除线程本地变量。注意在线程池的线程复用场景中在线程执行完毕时一定要调用remove避免在线程被重新放入线程池中时被本地变量的旧状态仍然被保存。 Question1为什么get()、remove()方法没有入参 Question2ThreadLocal对象作为key存在Thread中实际中一个线程可能会经过很多方法如何从Thread中获取到ThreadLocal的数据呢threadlocal如何使用没有ThreadLocal时Thread能获取到存储的数据吗 ThreadLocal#set(T value)方法
public void set(T value) {// 获取当前线程Thread t Thread.currentThread();// 获取从当前线程中获取ThreadlocalMap如果不为空存入值(key为当前threadlocal对象实例value为值)为空则创建一个ThreadLocaMapkey是当前的ThreadLocal ThreadLocalMap map getMap(t);if (map ! null)map.set(this, value);elsecreateMap(t, value);
}// 从Thread中获取ThreadLocalMap
ThreadLocalMap getMap(Thread t) {return t.threadLocals;
}
// 创建ThreadLocalMap 并赋值给Thread的threadLocals属性
void createMap(Thread t, T firstValue) {t.threadLocals new ThreadLocalMap(this, firstValue);
}ThreadLocal#get()方法
// 以当前ThreadLocal对象为key从Map中获取数据数据不为空直接返回数据数据为空则创建一个key是当前ThreadLocal、value为null的数据存入Map中并返回null
public T get() {Thread t Thread.currentThread();ThreadLocalMap map getMap(t);if (map ! null) {ThreadLocalMap.Entry e map.getEntry(this);if (e ! null) {SuppressWarnings(unchecked)T result (T)e.value;return result;}}return setInitialValue();
}
// 创建一个可以是当前ThreadLocal、value为null的数据存入Map中
private T setInitialValue() {T value initialValue();Thread t Thread.currentThread();// 获取线程中的threadLocals变量ThreadLocalMap map getMap(t);if (map ! null)map.set(this, value);elsecreateMap(t, value);return value;
}protected T initialValue() {return null;
}ThreadLocal#remove()方法
// 从map中移除当前ThreadLocal为key的数据
public void remove() {ThreadLocalMap m getMap(Thread.currentThread());if (m ! null)m.remove(this);}总结 如下图所示在每个线程内部都有一个名为 threadLocals 的成员变量该变量的类型为 ThreadLocalMap其中 key 为我们定义的 ThreadLocal变量的 this 引用value 则为我门使用 set方法设置的值。每个线程的本地变量存放在线程自己的内存变量threadLocals 中如果当前线程一直不消亡那么这些本地变量会一直存在所以可能会造成内存溢出因此使用完毕后要记得调用 ThreadLocal的 remove 方法删除对应线程的 threadLocals 中的本地变量。 使用说明
多个Thread与一个ThreadLocal
代码示例
ThreadLocalString content new ThreadLocal();// 线程1
Thread thread new Thread(() - {content.set(数据A);System.out.println(Thread.currentThread().getName() : content.get());
}, 线程1);
thread.start();// 线程2
Thread thread2 new Thread(() - {content.set(数据B);System.out.println(Thread.currentThread().getName() : content.get());}, 线程2);
thread2.start();输出结果
线程1:数据A
线程2:数据B为什么多个线程访问同一个ThreadLocal时数据不会乱呢也就是Thread2中不会获取到Thread1中存储的数据 本质还是因为跟前边给出的存储原理图有关同一个key数据存储在不同的Thread中在不同的Thread中访问肯定不会访问到其他Thread的数据
同一个Thread与多个ThreadLocal
本质上是一致的我们只需要记住真实数据是存放在**Thread**的**threadLocals**变量中的即可各个**Thread**中数据互不干扰。 多个ThreadLocal时仅仅是增加了Thread中**threadLocals**键值对的数量。
ThreadLocal local1 new ThreadLocal();
threadLocal.set(数据1);ThreadLocal local2 new ThreadLocal();
threadLocal2.set(数据2);ThreadLocal使用不当导致内存泄露 内存泄露问题指程序中动态分配的堆内存由于某种原因没有被释放或者无法释放造成系统内存的浪费导致程序运行速度减慢或者系统奔溃等严重后果。内存泄露堆积将会导致内存溢出。 ThreadLocal中TreadLocalMap的Entry继承了WeakReference。 当一个对象被WeakReference包装后它就产生了一个弱引用指向它。此时即使把强引用切断仍然有弱引用连接着。但是由于弱引用的特性这个对象会在下次被GC线程被直接回收。 本质不是**Entry**是弱应用而是**Entry**的**key**为弱引用。
使用弱引用的原因
强引用情况 theadlocal为null被回收时在Thread还存在的情况下依然存在到达ThreadLocal对象的引用链ThreadLocalMap中的key无法清除ThreadLocal的内容同时ThreadLocalMap中的Value也会保留。在使用线程池的情况下Thread可能一直存在可能会出现内存泄露。 弱应用情况 theadlocal为null被回收时在Thread还存在的情况下在下次GC时就会将ThreadLocal对象清除但是ThreadLocalMap中的Value还会保留导致无法删除。在使用线程池的情况下Thread可能一直存在可能会出现内存泄露。 Java中的内存泄露的情况长生命周期的对象持有短生命周期的对象的引用就很有可能发生内存泄露尽管短生命周期对象已经不再需要但是因为长生命周期对象持有它的引用而导致不能被回收这就是Java中内存泄露发生的场景。 避免内存泄露的方案
使用完ThreadLocal调用其remove方法删除对应的 Entry数据使用完ThreadLocal当前Thread也结束。【注意该方案在线程池中不行。】
阿里巴巴开发手册中强制规定
强引用与弱引用
在Java中有4种引用类型强、软、弱、虚。
强引用不受GC影响除非引用全部切断。比如 Student s new Student()假设当前只有s指向Student对象那么当snull时Student对象会在下次GC时被回收软引用对象会在内存不足触发GC时被回收适用于高速缓存弱引用是每次GC时都回收不论内存是否不足虚引用堆外内存比如zerocopy
ThreadLocal不支持继承
️演示说明
public class TestThreadLocal {// 创建线程变量public static ThreadLocalString threadLocal new ThreadLocal();public static void main(String[] args) {// 设置线程变量threadLocal.set(hello);// 启动子线程new Thread(() - {System.out.println(child thread: threadLocal.get());}, child thread).start();// 主线程输出线程变量值System.out.println(main: threadLocal.get());}}同一个 ThreadLocal 变量在父线程中被设置值后,在子线程中是获取不到的因为在子线程 thread 里面调用 get 方法时当前线程为 thread 线程而这里调用 set 方法设置线程变量的是 main 线程两者是不同的线程自然子线程访问时返回 null。那么有没有办法让子线程能访问到父线程中的值?答案是有可以通过InheritableThreadLocal实现
解决方案——InheritableThreadLocal类 InheritableThreadLocal可以解决父子线程中的变量共享。在最开始的类图中可以看到Thread类中除了有threadLoals变量还有inheritableThreadLocals变量。InheritableThreadLocal继承ThreadLocal重写了三个方法。操作Thread中的变量由threadLoals变为inheritableThreadLocals。 那么具体是如何实现的呢
参考资料 ThreadLocal原理及使用场景_小机double的博客-CSDN博客_threadlocal使用场景和原理ThreadLocal的内存泄露什么原因如何避免_BigHong123的博客-CSDN博客_threadlocal会导致内存泄露