Java同時プログラミング:ThreadLocalの理解

3065 ワード

前言
JAvaメモリモデル
JAva仮想マシンは、実際のコンピュータ構造を模倣し、java内の各変数、インスタンスにメモリ空間を割り当てる.主に次のものがあります.
  • メソッド領域:クラス情報、定数、静的変数、インスタントコンパイラコンパイル後のコードデータ.メソッド領域は、実行時定数プールを含む:字面量、シンボル参照
  • スタック:スレッド共有メモリ領域、オブジェクトインスタンス
  • を格納
  • プログラムカウンタ:
  • 仮想マシンスタック:スレッドプライベートデータ領域で、各メソッドが実行されるとスタックフレームが作成され、メソッドの変数テーブル、オペランドスタック、ダイナミックリンク、戻り値、戻りアドレスなどの情報が格納されます.
  • ローカルメソッドスタック:nativeコードに関連するスレッドプライベートデータ領域.

  • ThreadLocalは、各スレッドで変数のコピーを作成します.つまり、各スレッドの内部に変数があり、スレッドの内部のどこでも使用できます.スレッド間では互いに影響しません.これにより、スレッドのセキュリティの問題がなく、プログラムの実行性能にも深刻な影響を与えません.ThreadLocalは上記の問題を解決できますが、各スレッドにコピーが作成されているため、メモリの使用量がThreadLocalを使用しないよりも大きいなど、リソースの消費量を考慮します.
    ThreadLocalクラス
    ThreadLocalは、各変数のコピーをどのように作成しますか.
    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();
    }
    

    現在のスレッドのThreadLocalMapを取得し、取得に成功するとvalue値を返し、失敗するとsetInitialValue()を呼び出します.
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    

    threadLoaclsはThreadLoaclMapタイプで、ThreadLoaclクラスの内部クラスです.
    static class ThreadLocalMap {
    
        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference> {
            /** The value associated with this ThreadLocal. */
            Object value;
    
            Entry(ThreadLocal> k, Object v) {
                super(k);
                value = v;
            }
        }
        ....
    }
    

    EntryクラスはThreadLocalMapの内部クラスであり、WeakReferenceから継承され、キー値としてThreadLocalタイプが使用されます.
    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;
    }
    
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    
  • 各スレッドThreadの内部にはThreadLocal.ThreadLocalMapタイプのメンバー変数threadLocalsがあり、このthreadLocalsは実際の変数コピーを格納するために使用され、キー値は現在のThreadLocal変数であり、valueは変数コピー(すなわちTタイプの変数)
  • である.
  • ThreadではthreadLocalsが空であり、ThreadLocal変数によってget()メソッドまたはset()メソッドが呼び出されると、ThreadクラスのthreadLocalsが初期化され、現在のThreadLocal変数をキー値とし、ThreadLocalが保存するレプリカ変数をvalueとしてthreadLocalsに格納される.
  • 現在のスレッドで、コピー変数を使用する場合はgetメソッドでthreadLocalsで検索できます.

  • 一般的には、各スレッドは作成時に独自のThreadLocalタイプのプライベートオブジェクトを作成し、このプライベートオブジェクトをThreadのmapのkey値とすることで、対応するvalueを見つけることができ、スレッド間のvalueコピーが相互に影響しない役割を果たすことができます.