ThreadLocal源碼分析

什麼是ThreadLocal:

官網介紹:

* 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.

看另一個介紹:

The ThreadLocal class in Java enables you to create variables that can only be read
and written by the same thread. Thus, even if two threads are executing the same
code, and the code has a reference to a ThreadLocal variable, then the two threads
cannot see each other's ThreadLocal variables. 

簡單點說:就是ThreadLoacal類能夠創建被同一個線程進行讀和寫的變量。並且執行相同代碼並且代碼中包含ThreadLocal變量的引用的兩個不同線程不能互相看到對方的ThreadLocal的變量。

看了官網的介紹,我們會似懂非懂,那麼ThreadLocal是如何實現的呢?

ThreaLocal example
package com.dp.test;

import java.util.Scanner;
import java.util.concurrent.Executors;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        myThread t1=new myThread(false);
        myThread t2=new myThread(true);

        t1.setName("Thread1");
        t2.setName("Thread2");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }

}
class myThread extends Thread {

    private ThreadLocal<String> local=new ThreadLocal<String>(){

        @Override
        protected String initialValue() {
            // TODO Auto-generated method stub
            return "threadLocal的初始化";
        }

    };
    private boolean read=false;
    public myThread(boolean read){
        this.read=read;
    }
    public void run(){
        if(read==false){
            local.set(Thread.currentThread().getName()+"local");
            System.out.println(local.get());
        }else
        {
            System.out.println(local.get());
        }

    }
}

result》》》》》》》》 
Thread1local
threadLocal的初始化

從代碼的運行結果看出 每個線程都有自己的ThreadLocal變量,其中T1的設置爲Thread1local 而T2沒有set,當我們調用get時T1有,T2只有初始化的變量。現在我們來看看ThreadLocal的源碼吧!

ThreadLocal源碼分析 :

ThreadLocal 中的set()方法。

   public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

步驟:

  • 調用set()方法時先得到當前正在運行的線程t
  • 調用getMap(t)得到ThreadLocalMap 的對象
  ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

我們來看看t.threadLocals這個變量

 /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

threadLocals是Thread類中的一個成員變量。

  • 當map不爲空時則調用 map.set(this, value);那我們來看看這個方法
 private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (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 Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

該方法是ThreadLocal內部類ThreadLocalMap中的方法,有關於ThreadLocalMap的結構下次再講,我們可以類比喻hashmap,存入鍵值對的。

  • 如果map爲空,這是我們就的創建對象 createMap(t, value);
  void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

關於set方法的流程總結 :

  local.set(Thread.currentThread().getName()+"local");
   ---》getMap(t)得到ThreadLocalMap對象
         ---》t.threadLocals
              ---》threadLocals是Thread類中的一個成員變量
   ---》判斷map是否爲空
      --->不爲空:調用ThreadLocalMap的set方法
      ---》爲空:調用createMap方法

ThreadLocal的get方法

  public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

根據前面的講解我們應該對get方法中一些函數有點理解,對於get方法我就簡單講解下,

local.get()
 ---》調用getMap(t)得到ThreadLocalMap的對象
 ---》判斷map是否爲空
   ---》不爲空,調用map.getEntry(this);就是根據ThreadLocal得到Entry,得到其值。
   ---》爲空,調用setInitialValue()方法將初始值返回。

我們看看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;
    }

從方法體中,我們可以看出該方法做了兩件事,一用初始值創建ThreadLocalMap對象
二 返回初始值

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章