先上一張圖看一下ThreadLocal的內部結構,每個Thread對象內部都維護了一個ThreadLocal.ThreadLocalMap
我們在上圖看到的就是三個Thread對象內部格子的ThreadLocalMap
這里要說的不是ThreadLocal,是ThreadLocal為什么要用WeakReference
static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal<?>> { Entry(ThreadLocal<?> k, Object v) { super(k); value = v; }
弱引用WeakReference
弱引用只要發生了gc就會被回收,但前提是只有弱引用,沒有強引用(這點本身也不容易做到)
WeakReference<Apple> weakReference = new WeakReference<>(new Apple("1")); try { Thread.sleep(2000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(weakReference.get());//1 System.gc();//遇到gc就回收了 System.out.println(weakReference.get());//null
引用傳遞的特殊情況
1、傳遞的入參是null,方法內new了,也不會影響到外面null的對象,還是null的
2、傳遞的入參不是null,方法內=null了,也不會影響到外面不是null的對象
3、傳遞的入參不null,傳遞到線程中,線程運行中,外面把入參設置為null,線程內繼續不是null
總結,別管里面外面 null變!null !null變null 都不會隔層影響原來啥樣還啥樣
//我們在這里只插入一段模擬線程運行的情況
…… obj = new ObjectPassing(); obj.setName("name"); MyThread myThread = new MyThread(obj);//obj不是空的傳入新線程 new Thread(myThread).start();//線程不停的打印obj的name字段值 Thread.sleep(1000); obj = null;//將外面的obj置為空后,線程打印的仍然不為空,這點需要先明確 System.out.println("2:"+obj); Thread.sleep(1000000); } public void testNullPassFunc(ObjectPassing obj){ obj = new ObjectPassing(); obj.setName("zxp"); System.out.println("2:"+obj); } public void testNullPassFunc2(ObjectPassing obj){ obj = null; System.out.println("2:"+obj); } @Data static class ObjectPassing{ private String name; } 1:null 2:ParameterPassing.ObjectPassing(name=zxp) 3:null ======================= 1:ParameterPassing.ObjectPassing(name=name) 2:null 3:ParameterPassing.ObjectPassing(name=name) ======================= 1:name 1:name 2:null 1:name 1:name 1:name ……
public class MyThread implements Runnable { ParameterPassing.ObjectPassing objectPassing = null; public MyThread(ParameterPassing.ObjectPassing objectPassing){ this.objectPassing = objectPassing; } @Override public void run() { while (true){ System.out.println("1:"+objectPassing.getName()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }
分析ThreadLocal與WeakReference
分析一下為什么ThreadLocal要用WeakReference 不用有什么問題
ThreadLocal local = new ThreadLocal(); local.set("當前線程名稱:"+Thread.currentThread().getName());//將ThreadLocal作為key放入threadLocals.Entry中 Thread t = Thread.currentThread();//注意斷點看此時的threadLocals.Entry數組剛設置的referent是指向Local的,referent就是Entry中的key只是被WeakReference包裝了一下 local = null;//斷開強引用,即斷開local與referent的關聯,但Entry中此時的referent還是指向Local的,為什么會這樣,當引用傳遞設置為null時無法影響傳遞內的結果 System.gc();//執行GC t = Thread.currentThread();//這時Entry中referent是null了,被GC掉了,因為Entry和key的關系是WeakReference,並且在沒有其他強引用的情況下就被回收掉了 //如果這里不采用WeakReference,即使local=null,那么也不會回收Entry的key,因為Entry和key是強關聯 //但是這里僅能做到回收key不能回收value,如果這個線程運行時間非常長,即使referent GC了,value持續不清空,就有內存溢出的風險 //徹底回收最好調用remove //即:local.remove();//remove相當於把ThreadLocalMap里的這個元素干掉了,並沒有把自己干掉 System.out.println(local);