Threadlocal是為了,解決多線程環(huán)境下變量隔離的問題;
ThreadLocalMap
是threadLocal用來存儲數(shù)據(jù)的內(nèi)部類罢缸,他沒有實現(xiàn)任何集合接口矮冬,因為它僅供內(nèi)部使用豁鲤;
他是在threadLocal定義的,但是它的實例是在thread類中,是為每一個線程創(chuàng)建了一個Map而不是共享的map;
內(nèi)部定義了一個Entry玖详,key為ThreaLocal,value為存的值勤讽,對key使用了虛引用蟋座;
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
這里就是今天要探討的問題,之前一直卡在這個虛引用(虛引用垃圾回收條件是脚牍,回收時只要發(fā)現(xiàn)是虛引用就會直接回收掉)向臀,因為垃圾回收機制的存在我的第一個疑問是:
1、在線程運行時莫矗,會不會存在這個變量我放在threadLocal里面了飒硅,但是還沒使用然后垃圾回收運行,因為是key是虛引用所以被回收掉作谚,直白的說就是這個值我還沒用就被回收掉了三娩?
這個問題我查閱虛引用的資料了解到,一個對象是可以同時存在虛引用和強引用的妹懒,所以在作用域內(nèi)ThreadLocal的key存在強引用就不會被回收(ThreadLocalMap的key存放的是ThreadLocal對象雀监,而ThreadLocal使用時會new也就是強引用),見下面代碼
public class WeakReferenceDemo {
static WeakReference<String> weake;
public static void main(String[] args) {
test();
System.out.println("方法外====GC==========前");
System.out.println("獲取的值-" + weake.get());
System.gc();//已經(jīng)跳出了test的作用域眨唬,所以對象會被回收
System.out.println("方法外====GC==========后");
System.out.println("獲取的值-" + weake.get());
}
public static void test() {
String str = new String("weak_refrence");
weake = new WeakReference<String>(str);
System.out.println("方法里====GC==========前");
System.out.println("獲取的值-" + weake.get());
System.gc();//這個test方法的作用域里還存在str這個強引用会前,所以不會被回收;
System.out.println("方法里====GC==========后");
System.out.println("獲取的值-" + weake.get());
}
}
輸出結(jié)果:
方法里====GC==========前
獲取的值-weak_refrence
方法里====GC==========后
獲取的值-weak_refrence
方法外====GC==========前
獲取的值-weak_refrence
方法外====GC==========后
獲取的值-null
在threadLocal里面亦是如此匾竿,所以不會存在還沒使用就被回收的情況瓦宜,隨之而來的是下面的問題;
2岭妖、既然回收和作用域有關(guān)系临庇,那么我把這個強引用聲明為全局的是不是這個變量就一直存在了呢?見下面代碼:
public class ThreadLocalDemo01 {
//把ThreadLocal聲明為全局昵慌,保證他的生命周期在外部使用時還存活
static ThreadLocal<Integer> intThread = new ThreadLocal<>();
static ThreadLocal<String> strThread = new ThreadLocal<>();
public static void main(String[] args) {
//使用線程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 100, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(5));
int threadNum = 10;
//使用計數(shù)器假夺,使線程同步發(fā)生
CountDownLatch latch = new CountDownLatch(threadNum);
for (int i = 0; i < threadNum; i++) {
Thread t = new Thread(new RunableDemo01(latch));
executor.execute(t);
}
try {
latch.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
executor.shutdown();
System.out.println(intThread.get());//代碼1
System.out.println(strThread.get());//代碼2
}
}
class RunableDemo01 implements Runnable {
private CountDownLatch latch;
static Integer num = 1;
static String str = new String();
public RunableDemo01(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
method();
latch.countDown();
System.gc();
System.out.println();
}
public void method() {
ThreadLocalDemo01.intThread.set(num);
int n = ThreadLocalDemo01.intThread.get();
n++;
ThreadLocalDemo01.strThread.set(str);
String s = ThreadLocalDemo01.strThread.get();
s = Thread.currentThread().getName();
// System.out.println(n);
// System.out.println(s);
}
}
上面的代碼把threadLocal聲明為全局的,那么這個強引用在代碼1斋攀、2處應(yīng)該還是存在的已卷,所以就不會被垃圾回收機制處理掉;執(zhí)行代碼:
null
null
結(jié)果為null淳蔼,沒有取到值侧蘸,被回收呢?
確實是被回收了肖方,但是不是因為虛引用的問題被回收闺魏,而是整個map被回收了,原因分析:
這個問題之所以會出現(xiàn)俯画,其實是沒有好好理解ThreadLocal的源碼問題析桥,之前介紹了ThreadLocalMap它是定義在ThreadLocal里面的但是是在Thread里面實例的,所以他的生命周期是和線程相同的艰垂,因為在代碼1泡仗、2處已經(jīng)到了主線程了,而我們放值的時候是在子線程猜憎,1娩怎、2處子線程已經(jīng)結(jié)束,那么他們的ThreadLocalMap也已經(jīng)失效被回收了胰柑,主線程ThreadLocalMap有沒有放入這值所以返回null截亦;
以上就是目前對于Thread的探討爬泥,發(fā)現(xiàn)新問題將繼續(xù)加推。