ホームページ >Java >&#&チュートリアル >Java マルチスレッドでの ThreadLocal の使用

Java マルチスレッドでの ThreadLocal の使用

不言
不言転載
2018-10-17 16:21:532530ブラウズ

この記事の内容は、Java マルチスレッドでの ThreadLocal の使用に関するものです。必要な方は参考にしていただければ幸いです。

マルチスレッド環境では、synchronized メソッドを使用して HashMap# にアクセスするなど、非スレッドセーフ変数にアクセスするときにスレッド同期を実行する必要があります。 ## 実例。ただし、同期アクセスは同時実行性を低下させ、システムのパフォーマンスに影響を与えます。このとき、各スレッドに独立した変数を割り当てると、非スレッド セーフな変数を非同期的に使用できるようになり、そのような変数をスレッド ローカル変数と呼びます。

名前が示すように、スレッド ローカル変数は、各スレッドが変数の独自の独立したコピーを持ち、通常のローカル変数のように他のスレッドからアクセスできないことを意味します。

Java は言語レベルのスレッドローカル変数を提供していませんが、今回の主役である ThreadLocal クラスのクラスライブラリ内でスレッドローカル変数の機能を提供しています。

ThreadLocal の使用法

Java マルチスレッドでの ThreadLocal の使用
##ThreadLocal の Java8 バージョンには、上の図に示す 4 つがあります。 2 つのパブリック メソッドと 1 つの保護されたメソッドがあります。最初のメソッドは初期値 (デフォルトでは null) を返すために使用されます。 2 番目の静的メソッド withInitial(Supplier extends S>supplier) は Java8 バージョンで新しく追加されたもので、次の 3 つのインスタンス メソッドは非常に単純です。

Java8 より前では、ThreadLocal を使用するときに初期値を設定する場合は、ThreadLocal クラスを継承し、保護された T のinitialValue() メソッドをオーバーライドする必要があります。例:

ThreadLocal<integer> threadLocal = new ThreadLocal<integer>() {
    @Override
    protected Integer initialValue() {
        return 0;
    }
};</integer></integer>
##。 #Java8 版でも利用可能 新しく追加された静的メソッド withInitial(Supplier extends S>supplier) は、初期値を設定するのに非常に便利です。例えば:
ThreadLocal<integer> threadLocal = ThreadLocal.withInitial(() -> 0);

System.out.println(threadLocal.get());
threadLocal.set(16);
System.out.println(threadLocal.get());
threadLocal.remove();
System.out.println(threadLocal.get());

// 同一个线程的输出
0
16
0
Process finished with exit code 0</integer>

ThreadLocal の原理

では、ThreadLocal はスレッドローカル変数の機能をどのように実装するのでしょうか?実際、ThreadLocal の基本原理はそれほど複雑ではありません。 ThreadLocal は内部で静的クラス ThreadLocalMap を定義します。ThreadLocalMap のキーは ThreadLocal オブジェクトです。ただし、この ThreadLocalMap は ThreadLocal クラスに保持されます。 ThreadLocal のソース コードの一部を見てみましょう:

    // ThreadLocal的set方法
    public void set(T value) {
        // 获取当前线程对象
        Thread t = Thread.currentThread();
        // 获取Map
        ThreadLocalMap map = getMap(t);
        if (map != null)
            // 设置值
            map.set(this, value);
        else
            // 初始化Map
            createMap(t, value);
    }
    
    // ThreadLocal的createMap方法
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    
    // Thread类定义的实例域
    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal のコア実装は ThreadLocalMap の実装であり、内部でデータを格納する Entry クラスを宣言していることがわかります:

static class Entry extends WeakReference<threadlocal>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal> k, Object v) {
        super(k);
        value = v;
    }
}</threadlocal>
。 ThreadLocalMap の実装 HashMap の実装と類似点があります。たとえば、配列はデータの保存と自動展開にも使用されます。違いは、ハッシュ アルゴリズムとハッシュ衝突後の処理です。

        // ThreadLocalMap的set方法
        private void set(ThreadLocal> key, Object value) {

            Entry[] tab = table;
            int len = tab.length;
            // 计算在Entry[]中的索引,每个ThreadLocal对象都有一个hash值threadLocalHashCode,每初始化一个ThreadLocal对象,hash值就增加一个固定的大小0x61c88647
            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();
        }
        
        private static int nextIndex(int i, int len) {
            return ((i + 1 ThreadLocalMap が Entry[] 配列をリングとして扱っていることがわかります。計算したインデックスの位置を起点として、インデックスに既にデータがある場合はKeyが同じかどうかを判定し、同じであれば値を更新します。それ以外の場合は、空の位置が見つかるまで待って、そこに値を入れます。値を取得する場合も同様で、計算されたインデックスの位置から開始して、キーが同じであるかどうかを 1 つずつチェックします。ハッシュの衝突が多い場合、パフォーマンスがあまり良くない可能性があります。 <p></p><p>ThreadLocal のアプリケーション</p><p><strong></strong><code><br></code>##ThreadLocal のアプリケーションは非常に広範囲に渡ります。たとえば、Java エンジニアは次のようになります。 ThreadLocal は Spring フレームワークで非スレッドセーフのステートフル オブジェクトをカプセル化するために使用されるため、ほとんどの Bean をシングルトン スコープとして宣言できます。マルチスレッド コードを記述するときは、非スレッド セーフ ステートフル オブジェクトに同期的にアクセスする方が良いのか、それとも ThreadLocal を使用して非スレッド セーフ ステートフル オブジェクトをカプセル化する方が良いのかを考えることもできます。 </p><p><code><span style="font-family: 微软雅黑, Microsoft YaHei;"></span><br></code></p>

以上がJava マルチスレッドでの ThreadLocal の使用の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。