展览网站制作,什么牛网站建设,网站制作的公司哪个好,公司推进企业安全文化建设文章目录 ⚽ThreadLocal#x1f389;入门案例#x1f388;ThreadLocal在线程中怎么存储的#x1f397;为什么会造成内存泄漏#xff1f;#x1f383;ThreadLocalMap的key使用强引用和弱引用有什么区别呢#xff1f;#x1f514;补充说明Java中引用类型分类内存泄漏和内存… 文章目录 ⚽ThreadLocal入门案例ThreadLocal在线程中怎么存储的为什么会造成内存泄漏ThreadLocalMap的key使用强引用和弱引用有什么区别呢补充说明Java中引用类型分类内存泄漏和内存溢出区别 ⚽ThreadLocal 用来为每个线程提供独立的变量副本的 入门案例
使用案例
public class ThreadLocalMultipleExample {private static final ThreadLocalInteger threadLocal1 ThreadLocal.withInitial(() - 1);private static final ThreadLocalString threadLocal2 ThreadLocal.withInitial(() - Hello);public static void main(String[] args) {// 线程A设置值Thread threadA new Thread(() - {threadLocal1.set(10);threadLocal2.set(Thread A);System.out.println(Thread A - local1: threadLocal1.get()); // 10System.out.println(Thread A - local2: threadLocal2.get()); // Thread A});// 线程B设置值Thread threadB new Thread(() - {System.out.println(Thread B - local1: threadLocal1.get()); // 1System.out.println(Thread B - local2: threadLocal2.get()); // Hello});threadA.start();threadB.start();}
}运行结果可以看到虽然线程A修改了变量的值但是在线程B中变量的值还是初始给的值因为这两个变量在每个线程中都有自己的副本。
Thread A - local1: 10
Thread A - local2: Thread A
Thread B - local1: 1
Thread B - local2: HelloThreadLocal在线程中怎么存储的
先来了解ThreadLocal在Thread时怎么存储的。
我们先看Thread的源码里面有个ThreadLocalMap类型的变量。
public class Thread implements Runnable {ThreadLocal.ThreadLocalMap threadLocals null;
}从下面ThreadLocal的部分源码中可以看出这个ThreadLocalMap类是ThreadLocal类中静态内部类。我们可以把ThreadLocalMap当做一个Map其中ThreadLocal是key存储的值就是value。
可以看到ThreadLocal的set、get方法都是用Thread.currentThread()获取当前线程后拿到每个线程自己独有的ThreadLocalMap之后进行读写操作所以这里保证了每个线程都有自己的ThreadLocal副本。
public class ThreadLocalT {// ......省略部分代码static class ThreadLocalMap {static class Entry extends WeakReferenceThreadLocal? {Object value;Entry(ThreadLocal? k, Object v) {super(k);value v;}}}// ......省略部分代码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();}// ......省略部分代码public void set(T value) {Thread t Thread.currentThread();ThreadLocalMap map getMap(t);if (map ! null) {map.set(this, value);} else {createMap(t, value);}}
}ThreadLocalMap和HashMap在存储结构上有些不同HashMap是数组链表红黑树的形式但是ThreadLocalMap是纯数组的形式内部只有一个Entry[] table数组其中一个Entry就是一个键值对。
使用ThreadLocal时需要注意避免出现内存泄漏问题。
为什么会造成内存泄漏
我们从源码中可以看出ThreadLocalMap使用ThreadLocal的弱引用作为key如果一个ThreadLocal不存在外部强引用时Key(ThreadLocal)势必会被GC回收这样就会导致ThreadLocalMap中key为null 而value还存在着强引用只有当thead线程退出以后value的强引用链条才会断掉。意味着这个线程一直不结束的话这个value就一直无法回收造成内存泄漏这种情况一般发生在使用线程池的场景中因为里面的线程正常情况下会一直存活。
在平时使用ThreadLocal类时要避免内存泄漏问题可以在线程处理完任务后使用threadLocal.remove()方法移除当前threadLocal。
ThreadLocalMap的key使用强引用和弱引用有什么区别呢
key 使用强引用当ThreadLocalMap的key为强引用发生GC时因为ThreadLocalMap还持有ThreadLocal的强引用同时ThreadLocalMap和Thread生命周期相同如果没有手动删除ThreadLocal不会被回收导致Entry内存泄漏。key 使用弱引用当ThreadLocalMap的key为弱引用发生GC时由于ThreadLocalMap持有的是ThreadLocal的弱引用即使没有手动删除ThreadLocal也会被回收。当key为null在下一次其他ThreadLocal调用他们的set()get()remove()方法的时候都会清除key为null对应的value值。
补充说明 Java中引用类型分类
强引用我们常常 new 出来的对象就是强引用类型只要强引用存在垃圾回收器将永远不会回收被引用的对象哪怕内存不足的时候软引用使用 SoftReference 修饰的对象被称为软引用软引用指向的对象在内存要溢出的时候被回收弱引用使用 WeakReference 修饰的对象被称为弱引用只要发生垃圾回收若这个对象只被弱引用指向那么就会被回收虚引用虚引用是最弱的引用在 Java 中使用 PhantomReference 进行定义。虚引用中唯一的作用就是用队列接收对象即将死亡的通知
内存泄漏和内存溢出区别
内存泄漏 是因为程序没有释放不再使用的内存导致内存逐渐积累最终可能引起内存溢出。内存溢出 是当程序请求的内存超出了系统可分配的最大值时操作系统无法满足内存请求从而导致程序崩溃。