ThreadLocal是什么
线程本地变量。当使用 ThreadLocal 维护变量时, ThreadLocal 为每个使用该变量的线程提供独立的 变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程。
ThreadLocal原理
每个线程都有一个 ThreadLocalMap ( ThreadLocal 内部类),Map中元素的键为 ThreadLocal ,而 值对应线程的变量副本。
调用 threadLocal.set() -->调用 getMap(Thread) -->返回当前线程的 ThreadLocalMap --> map.set(this, value) ,this是 threadLocal 本身。源码如下
publicvoidset(Tvalue) { Threadt=Thread.currentThread(); ThreadLocalMapmap=getMap(t); if (map!=null) map.set(this, value); elsecreateMap(t, value); } ThreadLocalMapgetMap(Threadt) { returnt.threadLocals; } voidcreateMap(Threadt, TfirstValue) { t.threadLocals=newThreadLocalMap(this, firstValue); }
调用 get() -->调用 getMap(Thread) -->返回当前线程的 ThreadLocalMap -- > map.getEntry(this) ,返回 value 。源码如下:
publicTget() { Threadt=Thread.currentThread(); ThreadLocalMapmap=getMap(t); if (map!=null) { ThreadLocalMap.Entrye=map.getEntry(this); if (e!=null) { "unchecked") (Tresult= (T)e.value; returnresult; } } returnsetInitialValue(); }
threadLocals 的类型 ThreadLocalMap 的键为 ThreadLocal 对象,因为每个线程中可有多个 threadLocal 变量,如 longLocal 和 stringLocal 。
publicclassThreadLocalDemo { ThreadLocal<Long>longLocal=newThreadLocal<>(); publicvoidset() { longLocal.set(Thread.currentThread().getId()); } publicLongget() { returnlongLocal.get(); } publicstaticvoidmain(String[] args) throwsInterruptedException { ThreadLocalDemothreadLocalDemo=newThreadLocalDemo(); threadLocalDemo.set(); System.out.println(threadLocalDemo.get()); Threadthread=newThread(() -> { threadLocalDemo.set(); System.out.println(threadLocalDemo.get()); }); thread.start(); thread.join(); System.out.println(threadLocalDemo.get()); } }
ThreadLocal 并不是用来解决共享资源的多线程访问问题,因为每个线程中的资源只是副本,不会共 享。因此 ThreadLocal 适合作为线程上下文变量,简化线程内传参
ThreadLocal内存泄漏的原因?
每个线程都有⼀个 ThreadLocalMap 的内部属性,map的key是 ThreaLocal ,定义为弱引用,value是 强引用类型。垃圾回收的时候会⾃动回收key,而value的回收取决于Thread对象的生命周期。一般会通 过线程池的方式复用线程节省资源,这也就导致了线程对象的生命周期比较长,这样便一直存在一条强 引用链的关系: Thread --> ThreadLocalMap --> Entry --> Value ,随着任务的执行,value就有可能 越来越多且无法释放,最终导致内存泄漏。
解决⽅法:每次使⽤完 ThreadLocal 就调⽤它的 remove() ⽅法,手动将对应的键值对删除,从⽽避免 内存泄漏。
ThreadLocal使用场景有哪些?
ThreadLocal 适用场景:每个线程需要有自己单独的实例,且需要在多个方法中共享实例,即同时满足 实例在线程间的隔离与方法间的共享,这种情况适合使用 ThreadLocal 。比如Java web应用中,每个线 程有自己单独的 Session 实例,就可以使用 ThreadLocal 来实现。