Basic Of Concurrency(八: ThreadLocal)

ThreadLocal類在java中可以讓變量只能被相同的線程讀寫.那麼即便兩個線程同時訪問相同的代碼中的相同變量,也會產生兩個變量副本,變量副本分別僅對本線程可見. 在java中較爲典型的應用莫過於ServletHttpServletRequest了.html

建立一個ThreadLocal

done like this:java

private ThreadLocal myThreadLocal = new ThreadLocal();
複製代碼

以上代碼實例化了一個ThreadLocal, ThreadLocal在同一個線程中,僅須要被實例化一次便可.多個線程運行這段代碼時,每一個線程都會建立一個本身的ThreadLocal副本,且僅對本線程可見.當兩個線程往ThreadLocal設置不一樣值時,它們見不到對方設置的值.ide

訪問ThreadLocal

ThreadLocal建立完成後,能夠經過set()來存儲值.post

done like this:this

myThreadLocal.set("test var")
複製代碼

能夠經過get()來取得原先存儲的值.spa

done like this:線程

String myVar = (String)myThreadLocal.get();
複製代碼

調用get()返回以前存儲的值,調用set()方法,能夠傳遞一個Object做爲存儲值.code

ThreadLocal泛型化

咱們能夠建立一個泛型化的ThreadLocal,這樣在調用get()就不用進行類型強轉了.htm

done like this:繼承

private ThreadLocal myThreadLocal = new ThreadLocal<String>();
複製代碼

這樣咱們能夠直接經過set()來存儲目標類型值,經過get()來取得目標類型值:

myThreadLocal.set("text var");
String myVar = myThreadLocal.get();
複製代碼

設置ThreadLocal默認值

咱們知道經過調用set()來存儲值僅會對當前線程可見,對其餘線程不可見.有時候咱們須要設置對全部線程可見的默認值.

這時咱們能夠繼承ThreadLocal類並覆蓋initialValue().固然也能夠用使用匿名內部類的方式.

done like this:

private ThreadLocal myThreadLocal = new ThreadLocal<String>() {
    @Override protected String initialValue() {
        return "This is the initial value";
    }
}; 
複製代碼

如今全部線程在沒有調用set()的前提下,都能經過get()來取得默認值.

完整實例

public class ThreadLocalDemo {
    private ThreadLocal<Integer> myThreadLocal = new ThreadLocal();
    private AtomicInteger counter = new AtomicInteger();
    private Runnable runnable = () -> {
        Thread curThread = Thread.currentThread();
        int count = counter.getAndIncrement();
        System.out.println(curThread.getName() + ": set the val(" + count + ")");
        myThreadLocal.set(count);
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(curThread.getName() + ": get the val(" + myThreadLocal.get() + ")");
    };

    public static void main(String[] args) {
        ThreadLocalDemo tld = new ThreadLocalDemo();
        IntStream.range(1, 3)
                .mapToObj(i -> new Thread(tld.runnable, "Thread-" + i))
                .map(Thread::new)
                .forEach(Thread::start);
    }
}
複製代碼

咱們建立一個ThreadLocalDemo用於示範ThreadLocal的使用.首先咱們在內部實例化了ThreadLocal用於存儲int類型值,而後實例化AtomicInteger用於產生不一樣的整數,再而後就是定義咱們的Runnable.Runnable中前後調用ThreadLocalset()get().set()get()中間停頓了2s鍾,用於預留時間讓線程對ThreadLocal存儲值進行覆蓋,確保兩個線程都已經調用ThreadLocalset()後再調用get().預期結果是每一個線程經過get()取得都是以前經過set()存儲的值.

執行結果:

Thread-0: set the val(0)
Thread-1: set the val(1)
Thread-0: get the val(0)
Thread-1: get the val(1)

由結果能夠看出ThreadLocal存儲的值確實僅對本線程可見,若換成普通的變量結果應該是相似以下所示:

Thread-0: set the val(0)
Thread-1: set the val(1)
Thread-0: get the val(1)
Thread-1: get the val(1)

InheritableThreadLocal

InheritableThreadLocalThreadLocal的子類.與ThreadLocal不一樣的是InheritableThreadLocal存儲值容許當前線程建立的全部子線程訪問.

該系列博文爲筆者複習基礎所著譯文或理解後的產物,複習原文來自Jakob Jenkov所著Java Concurrency and Multithreading Tutorial

上一篇: volatile關鍵字
下一篇: 線程通信

相關文章
相關標籤/搜索