欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

解決子線程無(wú)法訪問父線程中通過ThreadLocal設(shè)置的變量問題

 更新時(shí)間:2024年07月08日 11:46:14   作者:沐雨橙風(fēng)ιε  
這篇文章主要介紹了解決子線程無(wú)法訪問父線程中通過ThreadLocal設(shè)置的變量問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

一、引出結(jié)論

學(xué)習(xí)過ThreadLocal的童鞋都知道,在子線程中,是無(wú)法訪問父線程通過ThreadLocal設(shè)置的變量的。

package thread;
 
/**
 * @author heyunlin
 * @version 1.0
 */
public class ThreadLocalExample {
 
    public static void main(String[] args) throws InterruptedException, NoSuchFieldException, IllegalAccessException {
        ThreadLocal<String> threadLocal = new ThreadLocal<>();
 
        threadLocal.set("hello");
 
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("run()...");
 
                /*
                 * 子線程中無(wú)法訪問父線程中設(shè)置的ThreadLocal變量
                 */
                System.out.println(threadLocal.get());
            }
        });
 
        thread.start();
        thread.join();
 
        System.out.println(thread);
        System.out.println(threadLocal.get());
    }
 
}

運(yùn)行結(jié)果:

InheritableThreadLocal就是為了解決這個(gè)不可見問題而生的~

package thread;
 
/**
 * @author heyunlin
 * @version 1.0
 */
public class InheritableThreadLocalExample {
 
    public static void main(String[] args) throws InterruptedException, NoSuchFieldException, IllegalAccessException {
        InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
 
        threadLocal.set("hello");
 
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("run()...");
                System.out.println(threadLocal.get());
            }
        });
 
        thread.start();
        thread.join();
 
        System.out.println(thread);
        System.out.println(threadLocal.get());
    }
 
}

運(yùn)行結(jié)果:

二、分析原因

那么, 究竟是什么原因?qū)е碌淖泳€程無(wú)法訪問父線程中的ThreadLocal變量呢,接下來(lái)研究ThreadLocal這個(gè)類的源碼。

先看一下這個(gè)ThreadLocal類上的文檔注釋,大概了解ThreadLocal是用來(lái)干什么的

/**
 * This class provides thread-local variables.  These variables differ from
 * their normal counterparts in that each thread that accesses one (via its
 * {@code get} or {@code set} method) has its own, independently initialized
 * copy of the variable.  {@code ThreadLocal} instances are typically private
 * static fields in classes that wish to associate state with a thread (e.g.,
 * a user ID or Transaction ID).
 *
 * <p>For example, the class below generates unique identifiers local to each
 * thread.
 * A thread's id is assigned the first time it invokes {@code ThreadId.get()}
 * and remains unchanged on subsequent calls.
 * <pre>
 * import java.util.concurrent.atomic.AtomicInteger;
 *
 * public class ThreadId {
 *     // Atomic integer containing the next thread ID to be assigned
 *     private static final AtomicInteger nextId = new AtomicInteger(0);
 *
 *     // Thread local variable containing each thread's ID
 *     private static final ThreadLocal&lt;Integer&gt; threadId =
 *         new ThreadLocal&lt;Integer&gt;() {
 *             &#64;Override protected Integer initialValue() {
 *                 return nextId.getAndIncrement();
 *         }
 *     };
 *
 *     // Returns the current thread's unique ID, assigning it if necessary
 *     public static int get() {
 *         return threadId.get();
 *     }
 * }
 * </pre>
 * <p>Each thread holds an implicit reference to its copy of a thread-local
 * variable as long as the thread is alive and the {@code ThreadLocal}
 * instance is accessible; after a thread goes away, all of its copies of
 * thread-local instances are subject to garbage collection (unless other
 * references to these copies exist).
 *
 * @author  Josh Bloch and Doug Lea
 * @since   1.2
 */

1、關(guān)鍵注釋

博主在類的文檔注釋上提取了幾個(gè)關(guān)于ThreadLocal的重要的說(shuō)明

This class provides thread-local variables.

這個(gè)類提供了線程本地變量

ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread.

ThreadLocal實(shí)例提供了可以關(guān)聯(lián)一個(gè)線程的靜態(tài)字段。

Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).

只要線程是存活狀態(tài),并且ThreadLocal對(duì)象可以訪問,每個(gè)線程都擁有一個(gè)線程本地變量的副本的引用;

在線程執(zhí)行完方法之后,所有線程本地變量都會(huì)被gc(垃圾收集器)回收,除非有其他變量引用了它。

2、研究源碼

經(jīng)過上面博主的大致翻譯,已經(jīng)對(duì)ThreadLocal有了一定的了解,接下來(lái)深入源碼去學(xué)習(xí)ThreadLocal的工作原理。

在不了解ThreadLocal的情況下,直接從我們直接調(diào)用的get()和set()方法入手。

public class ThreadLocal<T> {
 
    public T get() {
        Thread thread = Thread.currentThread();
        ThreadLocalMap map = getMap(thread);
 
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
 
            if (e != null) {
                T result = (T)e.value;
 
                return result;
            }
        }
 
        return setInitialValue();
    }
 
    public void set(T value) {
        Thread thread = Thread.currentThread();
        ThreadLocalMap map = getMap(thread);
 
        if (map != null) {
            map.set(this, value);
        } else {
            createMap(t, value);
        }
    }
 
}

set()方法

    public void set(T value) {
        // 獲取當(dāng)前線程
        Thread thread = Thread.currentThread();
        // 根據(jù)當(dāng)前線程獲取ThreadLocalMap對(duì)象
        ThreadLocalMap map = getMap(thread);
 
        // map不為null,設(shè)置初始值到map中
        if (map != null) {
            map.set(this, value);
        } else {
            // map是null,創(chuàng)建一個(gè)map
            createMap(t, value);
        }
    }
  • getMap()

看樣子和線程類Thead里的一個(gè)threadLocals變量有關(guān)。

    ThreadLocal.ThreadLocalMap getMap(Thread thread) {
        // 這個(gè)方法就是返回Thread的threadLocals變量的值
        return thread.threadLocals;
    }

ThreadLocalMap是ThreadLocal的一個(gè)靜態(tài)內(nèi)部類

public class ThreadLocal<T> {
 
    static class ThreadLocalMap {
    
    }
 
}
  • set()

ThreadLocal.ThreadLocalMap的set()方法,有點(diǎn)復(fù)雜,類似HashMap的底層源碼實(shí)現(xiàn)。

    private void set(ThreadLocal<?> key, Object value) {
        ThreadLocal.ThreadLocalMap.Entry[] tab = table;
        int len = tab.length;
        int i = key.threadLocalHashCode & (len-1);
 
        for (ThreadLocal.ThreadLocalMap.Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
            ThreadLocal<?> k = e.get();
 
            if (k == key) {
                e.value = value;
                
                return;
            }
 
            if (k == null) {
                replaceStaleEntry(key, value, i);
                
                return;
            }
        }
 
        tab[i] = new ThreadLocal.ThreadLocalMap.Entry(key, value);
        int sz = ++size;
        
        if (!cleanSomeSlots(i, sz) && sz >= threshold) {
            rehash();
        }
    }
  • createMap()
    void createMap(Thread thread, T firstValue) {
        // 初始化線程的threadLocals變量
        thread.threadLocals = new ThreadLocal.ThreadLocalMap(this, firstValue);
    }

set()方法總結(jié)

根據(jù)set()方法的源代碼,set()方法是把值設(shè)置到Thead線程類的threadLocals變量中,這個(gè)變量應(yīng)該是一個(gè)Map派生類的實(shí)例。

get()方法

    public T get() {
        Thread thread = Thread.currentThread(); // 獲取當(dāng)前線程
        
        // 通過getMap()方法獲取一個(gè)ThreadLocalMap對(duì)象
        // 因?yàn)檫@個(gè)類是以Map結(jié)尾的,推測(cè)是java.util.Map的派生類
        ThreadLocalMap map = getMap(thread);
 
        // 獲取到的map不為null
        if (map != null) {
            // 獲取Map的Entry
            // 說(shuō)明我們的推測(cè)大概率是正確的,因?yàn)镠ashMap中也有個(gè)Enty,而且也有value()方法
            ThreadLocalMap.Entry e = map.getEntry(this);
 
            // 獲取的value不為空,則轉(zhuǎn)為指定的類型T(泛型)直接返回
            if (e != null) {
                T result = (T) e.value;
 
                return result;
            }
        }
 
        // 代碼運(yùn)行到這里,說(shuō)明獲取到的map是null
        // 因?yàn)榍懊娴膇f中已經(jīng)通過return結(jié)束方法
        return setInitialValue();
    }
  • getMap()

這個(gè)方法的代碼在前面set()方法里已經(jīng)看了~

  • createMap()

這個(gè)方法的代碼在前面set()方法里已經(jīng)看了~

  • setInitialValue()

這個(gè)方法和set()方法的代碼幾乎一模一樣。知識(shí)多了一個(gè)返回值~

T value = initialValue();

set()方法的代碼

return value;
    private T setInitialValue() {
        // 通過initialValue()方法獲取一個(gè)初始化值
        T value = initialValue();
        
        // 獲取當(dāng)前線程
        Thread thread = Thread.currentThread();
        // 根據(jù)當(dāng)前線程獲取ThreadLocalMap對(duì)象
        ThreadLocalMap map = getMap(thread);
 
        // map不為null,設(shè)置初始值到map中
        if (map != null) {
            map.set(this, value);
        } else {
            // map是null,創(chuàng)建一個(gè)map
            createMap(thread, value);
        }
 
        // 返回初始值
        return value;
    }

get()方法總結(jié)

綜合上面關(guān)于get()方法的源代碼,get()方法是從Thead線程類的threadLocals變量中獲取數(shù)據(jù)。

ThreadLocal.ThreadLocalMap

下面是ThreadLocalMap類的一些關(guān)鍵的代碼,很顯然,這個(gè)內(nèi)部類的結(jié)構(gòu)設(shè)計(jì)的和HashMap非常相似,通過一個(gè)數(shù)組table保存值,根據(jù)傳入的key進(jìn)行特定的hash運(yùn)算得到數(shù)組的下標(biāo),然后獲取對(duì)應(yīng)數(shù)組下標(biāo)的值返回。

public class ThreadLocal<T> {
 
    static class ThreadLocalMap {
 
        static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;
 
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
 
                value = v;
            }
        }
 
        private static final int INITIAL_CAPACITY = 16;
 
        private ThreadLocal.ThreadLocalMap.Entry[] table;
 
        private int size = 0;
 
        private int threshold;
 
        private ThreadLocal.ThreadLocalMap.Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            ThreadLocal.ThreadLocalMap.Entry e = table[i];
            
            if (e != null && e.get() == key) {
                return e;
            } else {
                return getEntryAfterMiss(key, i, e);
            }
        }
    }
}

三、代碼總結(jié)

文章稍微學(xué)習(xí)的深入了一點(diǎn),可能有些童鞋已經(jīng)懵了,但是回到set()和get()方法的代碼可以發(fā)現(xiàn)

不管你存儲(chǔ)值的代碼如何復(fù)雜,都是通過當(dāng)前線程作為key存儲(chǔ)在ThreadLocal的靜態(tài)內(nèi)部類ThreadLocalMap中的,知道這點(diǎn)其實(shí)就夠了。

在子線程中,由于通過new Thread()新開了一個(gè)線程,那么當(dāng)代碼執(zhí)行這個(gè)新開的子線程的run()方法內(nèi)部,當(dāng)前線程已經(jīng)不是剛開始執(zhí)行方法的線程了。

比如:在main方法中,執(zhí)行代碼的線程是main線程,也就是主線程,它的線程名就是main。

但是新開一個(gè)線程,它的線程名是通過Threa-0開始編號(hào)的

通過一個(gè)簡(jiǎn)單的代碼了解一下

/**
 * @author heyunlin
 * @version 1.0
 */
public class Example {
 
    public static void main(String[] args) {
        System.out.println(Thread.currentThread());
 
        new Thread() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread());
            }
        }.start();
    }
 
}

運(yùn)行結(jié)果:

也就是說(shuō),在不同線程中,當(dāng)前線程已經(jīng)發(fā)生了改變,調(diào)用ThreaLocal的get()方法的key變了,自然獲取不到我們當(dāng)初設(shè)置的變量值。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 使用Java反射模擬實(shí)現(xiàn)Spring的IoC容器的操作

    使用Java反射模擬實(shí)現(xiàn)Spring的IoC容器的操作

    這篇文章主要介紹了使用Java反射模擬實(shí)現(xiàn)Spring的IoC容器的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • java 基礎(chǔ)之JavaBean屬性命名規(guī)范問題

    java 基礎(chǔ)之JavaBean屬性命名規(guī)范問題

    這篇文章主要介紹了java 基礎(chǔ)之JavaBean屬性命名規(guī)范問題的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • 詳談異步log4j2中的location信息打印問題

    詳談異步log4j2中的location信息打印問題

    這篇文章主要介紹了詳談異步log4j2中的location信息打印問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java 注解學(xué)習(xí)筆記

    Java 注解學(xué)習(xí)筆記

    這篇文章主要介紹了Java 注解的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下
    2021-03-03
  • Java elasticSearch-api的具體操作步驟講解

    Java elasticSearch-api的具體操作步驟講解

    這篇文章主要介紹了elasticSearch-api的具體操作步驟講解,本文通過詳細(xì)的步驟介紹和圖文代碼展示講解了該項(xiàng)技術(shù),需要的朋友可以參考下
    2021-06-06
  • java.sql.SQLException問題解決以及注意事項(xiàng)

    java.sql.SQLException問題解決以及注意事項(xiàng)

    這篇文章主要給大家介紹了關(guān)于java.sql.SQLException問題解決以及注意事項(xiàng)的相關(guān)資料,這個(gè)問題其實(shí)很好解決,文中通過圖文將解決的辦法介紹的很詳細(xì),需要的朋友可以參考下
    2023-07-07
  • SpringBoot實(shí)現(xiàn)優(yōu)雅停機(jī)的多種方式

    SpringBoot實(shí)現(xiàn)優(yōu)雅停機(jī)的多種方式

    優(yōu)雅停機(jī)(Graceful Shutdown)在現(xiàn)代微服務(wù)架構(gòu)中是非常重要的,它幫助我們確保在應(yīng)用程序停止時(shí),不會(huì)中斷正在進(jìn)行的請(qǐng)求或?qū)е聰?shù)據(jù)丟失,讓我們以通俗易懂的方式來(lái)講解這個(gè)概念以及如何在 Spring Boot 中實(shí)現(xiàn)它,需要的朋友可以參考下
    2025-01-01
  • java byte與base64的互轉(zhuǎn)的實(shí)現(xiàn)示例

    java byte與base64的互轉(zhuǎn)的實(shí)現(xiàn)示例

    在項(xiàng)目開發(fā)中經(jīng)常用到,比如前端上送文件流(byte[])到后臺(tái)并轉(zhuǎn)成文件,本文主要介紹了java byte與base64的互轉(zhuǎn)的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • Mybatis如何使用正則模糊匹配多個(gè)數(shù)據(jù)

    Mybatis如何使用正則模糊匹配多個(gè)數(shù)據(jù)

    這篇文章主要介紹了Mybatis如何使用正則模糊匹配多個(gè)數(shù)據(jù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Java中File文件操作類的基礎(chǔ)用法

    Java中File文件操作類的基礎(chǔ)用法

    這篇文章主要給大家介紹了關(guān)于Java中File文件操作類基礎(chǔ)用法的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用File類具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-07-07

最新評(píng)論