ThreadLocalの原理と使用シーン


一、ThreadLocal原理
ThreadLocalのset()メソッドを読めば、get()やremove()メソッドもわかりやすいので、set()メソッドを重点的に見てみましょう.
set()メソッド実行プロセスのまとめ:
1.現在のスレッドオブジェクトの取得
2.現在のスレッド・オブジェクトのメンバー変数ThreadLocalMapの取得
3.1 nullではなくset値
3.1.1現在のThreadLocalMapオブジェクトのEntry配列を取得する
3.1.2 ThreadLocalオブジェクトのi値の取得
3.1.3 Entry配列を遍歴し、ThreadLocalオブジェクトのi値から判断し、ThreadLocalオブジェクトが既に配列中にある場合、値を更新し、終了する
3.1.4配列は存在しない、key-value形式で:ThreadLocalオブジェクトと値valueを結合して配列に入れる
3.2 null、現在のスレッドのメンバー変数ThreadLocalMapを作成する
3.2.1 new 1つのEntry配列、初期容量はINITIAL_CAPACITY=16
3.2.2 Entry配列に格納されている位置i:ThreadLocalのthreadLocalHashCodeと15のビット演算を算出し、結果は型取りと同じである.
3.2.3 Entry配列を入れる
ソース分析は以下の通りです.
public class Thread implements Runnable {
    //                ThreadLocalMap
    ThreadLocal.ThreadLocalMap threadLocals = null;
}

public class ThreadLocal {
    private final int threadLocalHashCode = nextHashCode();
    public T get() {}
    public void remove() {}
    public void set(T value) {
        Thread t = Thread.currentThread();//      
        ThreadLocal.ThreadLocalMap map = getMap(t);//             ThreadLocalMap
        if (map != null)
            map.set(this, value);//  null,set 
        else
            createMap(t, value);// null,       ThreadLocalMap
    }
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocal.ThreadLocalMap(this, firstValue);//           ThreadLocalMap
    }

    static class ThreadLocalMap {
        private static final int INITIAL_CAPACITY = 16;
        static class Entry extends WeakReference> {
            //             
            Object value;
            // key-value  , ThreadLocal Object    。  ThreadLocal       Thread         Object。
            Entry(ThreadLocal> k, Object v) {
                super(k);
                value = v;
            }
        }

        //ThreadLocalMap        Entry    table
        private ThreadLocal.ThreadLocalMap.Entry[] table;

        ThreadLocalMap(ThreadLocal> firstKey, Object firstValue) {
            table = new ThreadLocal.ThreadLocalMap.Entry[INITIAL_CAPACITY];//new  Entry  ,     INITIAL_CAPACITY=16
            /**
             *       table     :ThreadLocal threadLocalHashCode 15    ,       。
             *     ThreadLocal     ,threadLocalHashCode final   ,    ThreadLocal   i     ,
             *         ThreadLocal       Thread         Object   
             */
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new ThreadLocal.ThreadLocalMap.Entry(firstKey, firstValue);//  Entry  
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

        private void set(ThreadLocal> key, Object value) {
            ThreadLocal.ThreadLocalMap.Entry[] tab = table;//  ThreadLocalMap   Entry  
            int len = tab.length;
            int i = key.threadLocalHashCode & (len - 1);//ThreadLocal   i 
            //  Entry  
            for (ThreadLocal.ThreadLocalMap.Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal> k = e.get();
                //ThreadLocal        ,   ,  
                if (k == key) {
                    e.value = value;
                    return;
                }
                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
            //      , key-value  : ThreadLocal value       
            tab[i] = new ThreadLocal.ThreadLocalMap.Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
    }
}

上記のソースコードに基づいて以下をまとめます.
1つのThreadオブジェクトに複数のThreadLocalオブジェクトがスレッドのローカルメモリに値を格納し、1つのThreadLocalオブジェクトが複数のスレッドのローカルメモリに値を格納します.だからThreadとThreadLocalは簡単に多対多関係と理解できる.(スレッドのローカルメモリはスレッドのワークメモリとも呼ばれ、JMMメモリモデル定義の概念)
ThreadLocalの本質はツールクラスであり、set()、get()、remove()などの方法を提供し、スレッドのローカルメモリの値の操作を提供する.Threadのメンバー変数ThreadLocalMapはスレッドのローカルメモリデータ格納を実現し(ポイント:メンバー変数はローカルメモリオブジェクトの格納を実現する)、ThreadLocalMapはThreadLocalに定義された静的内部クラスである.
ThreadLocalMapはEntryタイプの配列を維持し、EntryはThreadLocalMapに定義された静的内部クラスであり、EntryはObjectタイプのメンバー変数valueを使用して値を格納し、Entryの構造関数は本ThreadLocalオブジェクトとスレッド値valueをkey-value形式でバインドします.ThreadLocalの対応するEntry[]配列の下付きiは、ThreadLocalのhash値がfinalであるため、唯一変わらない.
二、ThreadLocal使用シーン
SynchronizedとThreadLocalは、マルチスレッド同時データアクセスを解決するために使用されますが、Synchronizedはスレッド間のデータ共有に使用され、ThreadLocalはスレッド間のデータ分離に使用されます.だから、シーンを使うには、この変数自体が共有されるべきかどうかによって!
ThreadLocalでは、データベース接続、セッション管理などを解決するためにシーンをよく使用します.springの管理を参照できます.例:1つの変数はメンバー変数(マルチスレッド共有)ですが、本質的にスレッドがプライベートであるべきで、ThreadLocalで共有メンバー変数copyをスレッドプライベート変数に1部使用できます.次のようにDemo:
/**
*   :     MyThreadLocal  ,sendMessage()     ,       sendMessage()        ?
*  :      connection      。   ThreadLocal             
**/
public class MyThreadLocal {
    //    ,     
    private Connection connection;

    public static ThreadLocal threadLocal = new ThreadLocal();

    public void sendMessage() throws SQLException {
        //Statement statement = connection.createStatement();
        Statement statement = getConnection().createStatement();
        //...
    }

    public static Connection getConnection() {
        Connection conn = threadLocal.get();//        value  
        if (conn == null) {
            Connection conn = ConnectionManager.getConnection();
            threadLocal.set(conn);
            return conn;
        } else {
            return conn;
        }
    }
}