Java ThreadLocal 理解

ThreadLocal 概念:java

ThreadLocal不是用來解決對象共享訪問的問題,而主要是提供了保存對象的方法和避免參數傳遞的方便的對象訪問方式。多線程

ThreadLocal並非一個Thread,而是Thread的局部變量,當使用ThreadLocal維護變量的時候ThreadLocal爲每個使用該變量的線程提供了獨立的變量副本,也就是說每個線程均可以單獨改變本身的副本,而不會影響其餘線程的副本。ide

從線程的角度來看目標變量就是當前線程的本地變量,這也就是類名Local的含義。測試

ThreadLocal的4個方法:this

  void set(T value)  將此線程局部變量的當前線程副本中的值設置爲指定值spa

  void remove()  移除此線程局部變量當前線程的值線程

  protected T initialValue()  返回此線程局部變量的當前線程的初始值指針

  T get()  返回此線程局部變量的當前線程副本中的值對象

注意 :默認狀況下 initValue(), 返回 null 。線程在沒有調用 set 以前,第一次調用 get 的時候, get方法會默認去調用 initValue 這個方法。因此若是沒有覆寫這個方法,可能致使 get 返回的是 null 。固然若是調用過 set 就不會有這種狀況了。可是每每在多線程狀況下咱們不能保證每一個線程的在調用 get 以前都調用了set ,因此最好對 initValue 進行覆寫,以避免致使空指針異常。blog

ThreadLocal實現原理:

ThreadLocal是如何作到爲每個線程維護變量的副本呢?讓咱們來看看ThreadLocal的源碼:

public class ThreadLocal<T> {

    public T get() {
        // 獲取當前線程
        Thread t = Thread.currentThread();
        // 獲取當前線程的threadLocals變量
        ThreadLocalMap map = getMap(t);
        // 從當前線程的threadLocals變量中取得本threadLocal爲key的值
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T) e.value;
        }
        return setInitialValue();
    }

    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }


    public void set(T value) {
        // 獲取當前線程
        Thread t = Thread.currentThread();
        // 獲取當前線程的threadLocals變量
        ThreadLocalMap map = getMap(t);
        // 以本threadLocal爲key的保存值到當前線程的threadLocals變量中去
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
}

在ThreadLocal類中有一個map,用於存儲沒一個線程的變量副本,map中的元素key爲線程對象 value爲線程副本中的變量值

示例:

package cn.com.example;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by Jack on 2017/2/13.
 */
public class ThreadLocalTest {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 3; i++) {
            executorService.execute(new ThreadTest());
        }

        executorService.shutdownNow();
    }
}


class ThreadTest implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " value=" + ThreadLocalNumber.get());
        }
    }
}

class ThreadLocalNumber {
    private static ThreadLocal<Integer> value = new ThreadLocal<Integer>() {
        protected synchronized Integer initialValue() {
            return 0;
        }
    };

    public static void set() {
        value.set(value.get() + 1);
    }

    public static int get() {
        // 爲了測試 直接在get裏面 ++
        value.set(value.get() + 1);
        return value.get();
    }
}

結果:

pool-1-thread-3 value=1
pool-1-thread-2 value=1
pool-1-thread-1 value=1
pool-1-thread-2 value=2
pool-1-thread-3 value=2
pool-1-thread-2 value=3
pool-1-thread-1 value=2
pool-1-thread-2 value=4
pool-1-thread-2 value=5
pool-1-thread-3 value=3
pool-1-thread-3 value=4
pool-1-thread-3 value=5
pool-1-thread-1 value=3
pool-1-thread-1 value=4
pool-1-thread-1 value=5

從運行結果得知 各線程都用於各自的Local變量,並各自讀寫互不干擾。

相關文章
相關標籤/搜索