Java-ThreadLocal虛引用取值問題分析

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ù)加推。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容