Threadlocal是為了,解決多線程環(huán)境下變量隔離的問題;
ThreadLocalMap
是threadLocal用來存儲(chǔ)數(shù)據(jù)的內(nèi)部類,他沒有實(shí)現(xiàn)任何集合接口,因?yàn)樗鼉H供內(nèi)部使用;
他是在threadLocal定義的,但是它的實(shí)例是在thread類中,是為每一個(gè)線程創(chuàng)建了一個(gè)Map而不是共享的map;
內(nèi)部定義了一個(gè)Entry,key為ThreaLocal,value為存的值,對(duì)key使用了虛引用;
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
這里就是今天要探討的問題,之前一直卡在這個(gè)虛引用(虛引用垃圾回收條件是,回收時(shí)只要發(fā)現(xiàn)是虛引用就會(huì)直接回收掉),因?yàn)槔厥諜C(jī)制的存在我的第一個(gè)疑問是:
1、在線程運(yùn)行時(shí),會(huì)不會(huì)存在這個(gè)變量我放在threadLocal里面了,但是還沒使用然后垃圾回收運(yùn)行,因?yàn)槭莐ey是虛引用所以被回收掉,直白的說就是這個(gè)值我還沒用就被回收掉了?
這個(gè)問題我查閱虛引用的資料了解到,一個(gè)對(duì)象是可以同時(shí)存在虛引用和強(qiáng)引用的,所以在作用域內(nèi)ThreadLocal的key存在強(qiáng)引用就不會(huì)被回收(ThreadLocalMap的key存放的是ThreadLocal對(duì)象,而ThreadLocal使用時(shí)會(huì)new也就是強(qiáng)引用),見下面代碼
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的作用域,所以對(duì)象會(huì)被回收
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();//這個(gè)test方法的作用域里還存在str這個(gè)強(qiáng)引用,所以不會(huì)被回收;
System.out.println("方法里====GC==========后");
System.out.println("獲取的值-" + weake.get());
}
}
輸出結(jié)果:
方法里====GC==========前
獲取的值-weak_refrence
方法里====GC==========后
獲取的值-weak_refrence
方法外====GC==========前
獲取的值-weak_refrence
方法外====GC==========后
獲取的值-null
在threadLocal里面亦是如此,所以不會(huì)存在還沒使用就被回收的情況,隨之而來的是下面的問題;
2、既然回收和作用域有關(guān)系,那么我把這個(gè)強(qiáng)引用聲明為全局的是不是這個(gè)變量就一直存在了呢?見下面代碼:
public class ThreadLocalDemo01 {
//把ThreadLocal聲明為全局,保證他的生命周期在外部使用時(shí)還存活
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;
//使用計(jì)數(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聲明為全局的,那么這個(gè)強(qiáng)引用在代碼1、2處應(yīng)該還是存在的,所以就不會(huì)被垃圾回收機(jī)制處理掉;執(zhí)行代碼:
null
null
結(jié)果為null,沒有取到值,被回收呢?
確實(shí)是被回收了,但是不是因?yàn)樘撘玫膯栴}被回收,而是整個(gè)map被回收了,原因分析:
這個(gè)問題之所以會(huì)出現(xiàn),其實(shí)是沒有好好理解ThreadLocal的源碼問題,之前介紹了ThreadLocalMap它是定義在ThreadLocal里面的但是是在Thread里面實(shí)例的,所以他的生命周期是和線程相同的,因?yàn)樵诖a1、2處已經(jīng)到了主線程了,而我們放值的時(shí)候是在子線程,1、2處子線程已經(jīng)結(jié)束,那么他們的ThreadLocalMap也已經(jīng)失效被回收了,主線程ThreadLocalMap有沒有放入這值所以返回null;
以上就是目前對(duì)于Thread的探討,發(fā)現(xiàn)新問題將繼續(xù)加推。