簡單學:併發編程之 ThreadLocal

關於java併發編程的相關文章都是閱讀了《java併發編程實戰》以後的讀書筆記總結java

概述

ThreadLocal實際上是線程封閉的一種規範化的實現,它經過提供一組get和set的接口爲每一個使用該變量的線程保存一份獨立的副本。對於那種按線程多實例(每一個線程對應一個實例)的對象的訪問,而且這個對象不少地方都要用到的狀況(例如數據庫鏈接管理、會話session管理以及線程私有的消息隊列等),ThreadLocal就會展示出它的魅力數據庫

下面的這個小例子展現了ThreadLocal的常規使用:編程

public class ThreadResource {

    private String threadName;
    private int threadId;

    public ThreadResource(int threadId, String threadName) {
        this.threadId = threadId;
        this.threadName = threadName;
    }

    public String getThreadName() {
        return threadName;
    }

    public void setThreadName(String threadName) {
        this.threadName = threadName;
    }

    public int getThreadId() {
        return threadId;
    }

    public void setThreadId(int threadId) {
        this.threadId = threadId;
    }
}

public class Test {
    //以一個靜態實例的方式持有一個ThreadLocal對象,它裏面以map的形式存儲了線程的局部變量
    static ThreadLocal<ThreadResource> resoursePackage = new ThreadLocal<ThreadResource>() {
        @Override
        protected ThreadResource initialValue() {
            return new ThreadResource(0, "initialThread");
        }
    };

    private static class TestThread extends Thread {
        @Override
        public void run() {
            resoursePackage.set(new ThreadResource(1, "testThread"));
            System.out.println(resoursePackage.get().getThreadName() + resoursePackage.get().getThreadId());
        }
    }

    public static void main(String[] args) {
        System.out.println(resoursePackage.get().getThreadName() + resoursePackage.get().getThreadId());
        new TestThread().start();
    }
}複製代碼

源碼解析ThreadLocal的實現

  • get()方法的實現
public T get() {
    //獲取當前threadlocal變量所屬的線程
    Thread t = Thread.currentThread();
    //根據線程獲取到一個ThreadLocalMap的對象
    ThreadLocalMap map = getMap(t);
    //若是線程已經綁定了一個ThreadLocalMap對象的話,則從中獲取到裏面所保存的值,不然使用初始化的值
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T) e.value;
            return result;
        }
    }
    return setInitialValue();
}複製代碼

咱們看一下上面getMap()的方法的實現session

ThreadLocalMap getMap(Thread t) {
    //返回線程的一個ThreadLocalMap的成員變量,下面是該成員變量在threa類中的聲明
    //ThreadLocal values pertaining to this thread. This map is maintained by the ThreadLocal class.
    //ThreadLocal.ThreadLocalMap threadLocals = ull;
    return t.threadLocals;
}複製代碼

再看一下線程還沒有綁定ThreadLocalMap對象的時候,調用的 setInitialValue() 的方法的實現併發

private T setInitialValue() {
    //initialValue()就是咱們在新建一個ThreadLocal變量的時候,實現的protected的那個方法。
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        //由此咱們可知,線程還沒有綁定到ThreadLocalMap對象的時候
        //ThreadLocal爲咱們使用在initialValue設置的值初始化了一個對象值,並綁定到該線程上。
        createMap(t, value);
    return value;
}複製代碼

在上面的代碼中,咱們一直說起到了一個ThreadLocalMap的類,它實際上是在ThreadLocal的一個靜態內部類,它是一個ThreadLocal自定義的hash map對象,用於保存線程的局部變量。在它裏面,又包含了一個Entry的靜態內部類,它裏面就是對應的所要存儲的值。咱們看一下源碼的實現ide

static class ThreadLocalMap {
    static class Entry extends WeakReference<ThreadLocal<?>> {
        Object value;
        //以ThreadLocal對象做爲鍵值,保存threadlocal變量所包含的值
        //咱們在ThreadLocal的get方法當中也是根據threadlocal變量取出所存儲的值
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
}複製代碼
  • set()方法的實現
public void set(T value) {
    //獲取threadlocal對象所屬的線程
    Thread t = Thread.currentThread();
    //獲取線程所綁定的ThreadLocalMap對象
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        //在線程還沒有初始化並綁定ThreadLocalMap對象的時候,使用給定的value值新建一個,並將線程與該對對象關聯起來
        createMap(t, value);
}複製代碼

在閱讀了上面的源碼以後,咱們大概已經明白了ThreadLocal是怎麼作到爲每一個線程保存一份引用對象的拷貝的值的。每一個thread對象都會持有一個ThreadLocal.ThreadLocalMap的對象的引用,而咱們經過ThreadLocal對象找到了線程所持有的這個ThreadLocalMap對象,並往其中添加、移除或得到咱們所要保存的引用對象的值。this

關於ThreadLocalMap裏面實現的自定義的hash map咱們能夠在ThreadLocal的源碼中深刻了解,這裏不作進一步的深刻。spa

相關文章
相關標籤/搜索