いつ、どのようにThreadLocal変数を使用する必要がありますか?


877

いつThreadLocal変数を使用すればよいですか?

使い方は?


11
ThreadLocalを使用している場合は、ラッパーを決して記述しないでください!!! 一人ひとりの変数を使用したい開発者はそれはThreadLocalの'です知っている必要があります
ないバグ

2
@Notabug明示的に指定する場合は、ThreadLocal値を処理するように変数に名前を付けることができますRequestUtils.getCurrentRequestThreadLocal()。ただし、これは非常に洗練されているとは言えませんが、これは、ほとんどの場合、ThreadLocal自体があまり洗練されていないためです。
whirlwin

@SashaはThreadLocalのは、ストア実行トレースに使用することができます
のGaurav

回答:


864

考えられる(そして一般的な)使用法の1つは、スレッドセーフではないオブジェクトがあるが、そのオブジェクトへのアクセスの同期を避けたい場合です(私はSimpleDateFormatを調べています)。代わりに、各スレッドにオブジェクトの独自のインスタンスを割り当てます。

例えば:

public class Foo
{
    // SimpleDateFormat is not thread-safe, so give one to each thread
    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue()
        {
            return new SimpleDateFormat("yyyyMMdd HHmm");
        }
    };

    public String formatIt(Date date)
    {
        return formatter.get().format(date);
    }
}

ドキュメンテーション


160
同期またはthreadlocalの別の方法は、変数をローカル変数にすることです。ローカル変数は常にスレッドセーフです。DateFormatsは作成にコストがかかるため、ローカルにすることは悪い習慣だと思いますが、このトピックに関する確かなメトリックスを見たことがありません。
ジュリアンチャスタン2009年

18
これはハッキングの代償として高額SimpleDateFormatです。おそらく、スレッドセーフな代替手段を使用する方が良いでしょう。あなたがその同意する場合はシングルトンが悪い、その後ThreadLocalさらに悪くなります。
Alexander Ryzhov 2013年

4
@overthink ThreadLocalをstaticかつfinalと宣言する理由はありますか?パフォーマンスなどですか?
Sachin Gorade 2015

4
ThreadLocal.get()メソッドは、スレッドごとにThreadLocal.initialValue()を呼び出します(つまり、SimpleDateFormatオブジェクトがスレッドごとに作成されます)。SimpleDateFormatをローカル変数として使用する方がいいのではないですか(ガベージコレクションの問題に対処する必要がないため)。
sudeepdino008 2016年

3
SimpleDateFormatに二重ブレース初期化を使用しないように注意してください。これにより匿名クラスが作成され、クラスローダーがガベージコレクションされなくなります。メモリリーク:新しいSimpleDateFormat(){{applyPattern( "yyyyMMdd HHmm")}}を返す;
user1944408

427

a ThreadLocalは指定された内のデータへの参照であるため、スレッドプールを使用するアプリケーションサーバーでをThread使用すると、クラスローディングリークが発生する可能性がありThreadLocalます。のクリーンアップやのメソッドの使用については、十分に注意する必要がありThreadLocalます。get()set()ThreadLocalremove()

完了時にクリーンアップしないと、デプロイされたWebアプリケーションの一部としてロードされたクラスへの参照が永続ヒープに残り、ガベージコレクションは行われません。webappが所有するものではないThreadため、webappを再デプロイ/アンデプロイしても、webappのクラスへの各の参照はクリーンアップされThreadません。連続するデプロイメントごとに、クラスの新しいインスタンスが作成され、ガベージコレクションは行われません。

java.lang.OutOfMemoryError: PermGen spaceいくつかのグーグルが原因でメモリ不足の例外が発生し-XX:MaxPermSize、バグを修正する代わりにおそらく増加するだけです。

これらの問題が発生した場合は、EclipseのMemory Analyzerを使用するか、Frank Kievietのガイドフォローアップに従って、どのスレッドとクラスがこれらの参照を保持しているかを判断できます。

更新:私が抱えていたいくつかの問題を追跡するのに役立つAlex Vasseurのブログエントリを再発見しましたThreadLocal


11
Alex Vasseurがブログを移動しました。これは、メモリリークに関する記事への現在のリンクです。
ケンスター2012年

12
ジュリアンがリンクしているスレッドはここに移動しようで、読む価値があります…
ドナルフェロー

28
これは、有益な回答ではありますが、実際には質問に回答しない回答に対する非常に多くの賛成票です。
ロビン

8
PermGenがJava 8によって殺されたので、これはこの答えを何らかの方法で変更しますか?
邪悪な洗濯機

13
@ロビン:私は同意しません。質問は、ThreadLocalを正しく使用する方法に関するものであり、概念(ここではThreadLocal)を完全に理解するには、それを使用しない方法と不注意に使用するリスクを理解することも重要です。それがPhilの答えです。他の回答ではカバーされていないその点を彼がカバーしてくれてうれしいです。これらの投票はすべて当然のことです。SOは単なるQAサイトではなく、概念の理解を構築する必要があると思います。
Saurabh Patil 2017年

166

多くのフレームワークはThreadLocalsを使用して、現在のスレッドに関連するいくつかのコンテキストを維持しています。たとえば、現在のトランザクションがThreadLocalに格納されている場合、スタックの下位の誰かがアクセスする必要がある場合に備えて、すべてのメソッド呼び出しを通じてパラメーターとして渡す必要はありません。Webアプリケーションは、現在の要求とセッションに関する情報をThreadLocalに保存し、アプリケーションがそれらに簡単にアクセスできるようにします。Guiceを使用すると、注入されたオブジェクトのカスタムスコープ(Guiceのデフォルトのサーブレットスコープ)を実装するときにThreadLocalsを使用できますもおそらくそれらを使用します)。

ThreadLocalsは一種のグローバル変数です(ただし、1つのスレッドに制限されているため、害は少し少なくなります)。不要な副作用やメモリリークを回避するために、これらを使用する場合は注意が必要です。ThreadLocal値が不要になったときに常に自動的にクリアされ、APIの不正な使用が不可能になるように(たとえば、このように)APIを設計します。ThreadLocalsは、コードクリーナーを作るために使用することができ、そしていくつかのまれなケースでは、彼らは何かを動作させるための唯一の方法です(私の現在のプロジェクトは、2つのこのような例があった。それらは文書化され、ここで、「静的フィールドとグローバル変数」の下)。


4
これが、exPOJOフレームワーク(www.expojo.com)が、アノテーションやインジェクションのオーバーヘッドを必要とせずにORM Session / PersistenceManagerにアクセスできるようにする方法です。「オブジェクトインジェクション」ではなく、「スレッドインジェクション」のようなものです。依存関係を必要とする可能性のあるすべてのオブジェクトにそれらを埋め込むという要件(およびオーバーヘッド)なしで、依存関係へのアクセスを提供します。従来のDI(たとえば、Springなど)の代わりにスレッドインジェクションを使用すると、DIフレームワークを「軽量」にできることは驚くべきことです
Volksman

27
この重要な答えを見つけるために、なぜここまでスクロールしなければならなかったのですか?
Jeremy Stein

1
@Esko、プロジェクトでは、ハッシュコードおよび等号でのThreadLocalの使用は不要です。より良いアプローチは、必要に応じてハッシュコード/等しい関数への参照を作成または渡すことです。これにより、ThreadLocalハックの必要性を完全に回避できます。
パセリエ2017

1
@JeremyStein、より良い答えは、stackoverflow.com /
a / 817911/632951

1
これは、TLの使用に関する最良の説明です。
デクスター

52

Javaでは、スレッドごとに異なるデータムがある場合、そのデータムを必要とする(または必要とする可能性がある)すべてのメソッドに渡すか、データムをスレッドに関連付けるかを選択できます。すべてのメソッドが共通の「コンテキスト」変数を渡す必要がある場合は、どこにでもデータを渡すことができます。

そうでない場合は、追加のパラメーターを使用してメソッドシグネチャを混乱させたくない場合があります。スレッド化されていない世界では、Javaのグローバル変数に相当する問題を解決できます。スレッド化された単語では、グローバル変数に相当するものはスレッドローカル変数です。


13
したがって、グローバルを回避するのと同じ方法で、スレッドローカルを回避する必要があります。値を渡す代わりにグローバル(スレッドローカル)を作成しても問題ないことを受け入れることはできません。多くの場合、修正したくないアーキテクチャの問題が明らかになるため、人々は気に入らないでしょう。
ファンメンデス

10
多分...しかし、どこにでも渡されなければならない新しいデータを追加しなければならない既存の巨大なコードベースがある場合、例えばセッションコンテキスト、dbトランザクション、ログインしたユーザーなどが役に立つかもしれません。。
コーネル・マッソン

6
データの代わりにデータを使用することに対する称賛。
深い

これは私を好奇心をそそられました。'datum' is the singular form and 'data' is the plural form.
シッダールタ

23

本のJava Concurrency in Practiceには非常に良い例があります。著者(Joshua Bloch)は、スレッドの制限がスレッドの安全性とThreadLocalを実現する最も簡単な方法の1つであると説明しています。がスレッドの制限を維持するためのより正式な方法であると説明しています。最後に、彼はそれをグローバル変数として使用することによって人々がどのようにそれを悪用することができるかについても説明します。

言及した本からテキストをコピーしましたが、ThreadLocalをどこで使用するかを理解することはそれほど重要ではないため、コード3.10が欠落しています。

スレッドローカル変数は、変更可能なシングルトンまたはグローバル変数に基づく設計での共有を防ぐためによく使用されます。たとえば、シングルスレッドアプリケーションは、すべてのメソッドにConnectionを渡さなくても済むように、起動時に初期化されるグローバルデータベース接続を維持する場合があります。JDBC接続はスレッドセーフではない可能性があるため、追加の調整なしでグローバル接続を使用するマルチスレッドアプリケーションもスレッドセーフではありません。ThreadLocalを使用してJDBC接続を格納することにより、リスト3.10のConnectionHolderのように、各スレッドは独自の接続を持ちます。

ThreadLocalは、アプリケーションフレームワークの実装で広く使用されています。たとえば、J2EEコンテナは、EJB呼び出しの間、トランザクションコンテキストを実行中のスレッドに関連付けます。これは、トランザクションコンテキストを保持する静的Thread-Localを使用して簡単に実装されます。フレームワークコードが現在実行中のトランザクションを判別する必要がある場合、このThreadLocalからトランザクションコンテキストをフェッチします。これは、すべてのメソッドに実行コンテキスト情報を渡す必要性を減らすという点で便利ですが、このメカニズムを使用するすべてのコードをフレームワークに結合します。

ThreadLocalのスレッド制限プロパティをグローバル変数を使用するライセンスとして、または「隠された」メソッド引数を作成する手段として扱うことにより、ThreadLocalを悪用するのは簡単です。グローバル変数と同様に、スレッドローカル変数は再利用性を損ない、クラス間に隠された結合を導入する可能性があるため、注意して使用する必要があります。


18

基本的に、あなたが必要とするとき、現在のスレッドに依存する変数の値を、それはあなたには、いくつかの他の方法でスレッドに値を添付するために便利ではありません(たとえば、スレッドのサブクラス)。

典型的なケースは、他のフレームワークがスレッドを作成した場合です、サーブレットコンテナーなど、コードが実行を、または変数が「変数ではなく論理的な場所」にあるため、ThreadLocalを使用するほうが理にかなっている場合です。 Threadサブクラスまたは他のハッシュマップにぶら下がっている)。

私のWebサイトで、さらに興味深いのは、ThreadLocalいつ使用するについてのいくつかの議論と例です

一部の人々は、スレッド番号が必要な特定の並行アルゴリズムで各スレッドに「スレッドID」を添付する方法としてThreadLocalを使用することを推奨しています(例:Herlihy&Shavitを参照)。そのような場合は、本当に利益を得ているか確認してください!


ThreadLocalのは、ストア実行トレースに使用することができます
のGaurav

13
  1. JavaのThreadLocalはJDK 1.2で導入されましたが、後でThreadLocal変数の型安全性を導入するためにJDK 1.5で生成されました。

  2. ThreadLocalはThreadスコープに関連付けることができます。Threadによって実行されるすべてのコードはThreadLocal変数にアクセスできますが、2つのスレッドはお互いにThreadLocal変数を参照できません。

  3. 各スレッドはThreadLocal変数の排他的コピーを保持します。これは、スレッドが終了または終了した後、通常または何らかの例外が原因でガベージコレクションに適格になります。これらのThreadLocal変数には他のライブ参照がありません。

  4. JavaのThreadLocal変数は通常、クラスのプライベート静的フィールドであり、スレッド内でその状態を維持します。

続きを読む:JavaのThreadLocal-サンプルプログラムとチュートリアル


ThreadLocalのは、ストア実行トレースに使用することができます
のGaurav

11

ドキュメントはそれを非常によく言っています:「(スレッドローカル変数]にアクセスする各スレッド(そのgetメソッドまたはsetメソッドを介して)は、独自の独立して初期化された変数のコピーを持っています」。

各スレッドが何かの独自のコピーを持つ必要がある場合に使用します。デフォルトでは、データはスレッド間で共有されます。


18
デフォルトでは、静的オブジェクト、または両方のスレッドが同じオブジェクトへの参照を持つスレッド間で明示的に渡されるオブジェクトが共有されます。スレッドでローカルに宣言されたオブジェクトは共有されません(スレッドのスタックに対してローカルです)。それを明確にしたかっただけです。
Ian Varley、

@IanVarley:それを指摘してくれてありがとう。MyThreadクラスで宣言されて使用されている変数がある場合、その場合はThreadLocalが必要ですか?例:クラスMyThread extends Thread {String var1 ;, int var2; この場合、var1とvar2はスレッド独自のスタックの一部になり、共有されません。この場合、ThreadLocalが必要ですか?完全に誤解しているかもしれませんが、ご案内ください。
Jayesh、2016年

4
各スレッドが何かの独自のコピーを持ちたい場合、なぜそれを単純にローカルと宣言できないのですか(常にスレッドセーフです)。
sudeepdino008 2016年

@ sudeepdino008これらのスレッド(webappフレームワーク、スレッドプールライブラリ)の作成を常に制御できるとは限りません
JMess

10

Webappサーバーはスレッドプールを保持する場合がありThreadLocal、クライアントへの応答の前に変数を削除する必要があるため、現在のスレッドは次のリクエストで再利用できます。


8

threadlocal変数を使用できる2つの使用例
-1-状態をスレッドに関連付ける必要がある場合(たとえば、ユーザーIDまたはトランザクションID)。これは通常、サーブレットに送信されるすべてのリクエストに一意のトランザクションIDが関連付けられているWebアプリケーションで発生します。

// This class will provide a thread local variable which
// will provide a unique ID for each thread
class ThreadId {
    // Atomic integer containing the next thread ID to be assigned
    private static final AtomicInteger nextId = new AtomicInteger(0);

    // Thread local variable containing each thread's ID
    private static final ThreadLocal<Integer> threadId =
        ThreadLocal.<Integer>withInitial(()-> {return nextId.getAndIncrement();});

    // Returns the current thread's unique ID, assigning it if necessary
    public static int get() {
        return threadId.get();
    }
}

ここでメソッドwithInitialはラムダ式を使用して実装されていることに注意してください。
2-もう1つの使用例は、スレッドセーフなインスタンスが必要で、同期を使用したくない場合です。同期によるパフォーマンスコストが高くなるためです。そのようなケースの1つは、SimpleDateFormatが使用される場合です。SimpleDateFormatはスレッドセーフではないため、スレッドセーフにするメカニズムを提供する必要があります。

public class ThreadLocalDemo1 implements Runnable {
    // threadlocal variable is created
    private static final ThreadLocal<SimpleDateFormat> dateFormat = new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue(){
            System.out.println("Initializing SimpleDateFormat for - " + Thread.currentThread().getName() );
            return new SimpleDateFormat("dd/MM/yyyy");
        }
    };

    public static void main(String[] args) {
        ThreadLocalDemo1 td = new ThreadLocalDemo1();
        // Two threads are created
        Thread t1 = new Thread(td, "Thread-1");
        Thread t2 = new Thread(td, "Thread-2");
        t1.start();
        t2.start();
    }

    @Override
    public void run() {
        System.out.println("Thread run execution started for " + Thread.currentThread().getName());
        System.out.println("Date formatter pattern is  " + dateFormat.get().toPattern());
        System.out.println("Formatted date is " + dateFormat.get().format(new Date()));
    } 

}

すべての目的が増分の一意の整数値を返すことである場合、例1でThreadLocalを使用して一意のIDを生成する利点はまだわかりませんでした。`` `public class ThreadId {//割り当てられる次のスレッドIDを含む原子整数final static final AtomicInteger nextId = new AtomicInteger(0); //現在のスレッドの一意のIDを返し、必要に応じて割り当てますpublic static int get(){return nextId..getAndIncrement(); }} `` `
dashenswen

7

Java 8リリース以降、初期化する宣言的な方法がさらにありますThreadLocal

ThreadLocal<Cipher> local = ThreadLocal.withInitial(() -> "init value");

Java 8がリリースされるまで、次のことを行う必要がありました。

ThreadLocal<String> local = new ThreadLocal<String>(){
    @Override
    protected String initialValue() {
        return "init value";
    }
};

さらに、使用されるクラスのインスタンス化メソッド(コンストラクター、ファクトリーメソッド)がThreadLocalパラメーターを取らない場合は、メソッド参照(Java 8で導入)を使用するだけで済みます。

class NotThreadSafe {
    // no parameters
    public NotThreadSafe(){}
}

ThreadLocal<NotThreadSafe> container = ThreadLocal.withInitial(NotThreadSafe::new);

注:が呼び出されるjava.util.function.Supplierときにのみ評価されるラムダを 渡しているがThreadLocal#get、値が以前に評価されていないため、評価は遅延です。


5

ThreadLocalパターンには十分注意する必要があります。Philのようにいくつかの主要な欠点がありますが、言及されなかったのは、ThreadLocalコンテキストを設定するコードが「再入可能」でないことを確認することです。

予期しないときにスレッドの情報が変化し始める可能性があるため、情報を設定するコードが2回または3回実行されると、悪いことが発生する可能性があります。したがって、再設定する前に、ThreadLocal情報が設定されていないことを確認してください。


2
再入可能に対処するコードが用意されていれば、再入は問題になりません。開始時に、変数がすでに設定されているかどうかをメモし、終了時に、以前の値を復元するか(存在する場合)、削除するか(そうでない場合)。
スーパーキャット2014年

1
@ジェフ、デュード、それはThreadLocalパターンだけでなく、あなたが書くすべてのコードに当てはまります。その場合はF(){ member=random(); F2(); write(member); }、新しい値を持つF2オーバーライドメンバー、そして明らかにwrite(member)、もはやあなたがしている数書きませんrandom()編を。これは文字通り単なる常識です。同様に、あなたがそうするならF(){ F(); }、あなたの無限ループで幸運を祈ります!これはどこにでも当てはまり、に限定されませんThreadLocal
パセリエ

@Jeff、コード例?
パセリエ

5

いつ?

オブジェクトがスレッドセーフでない場合は、スケーラビリティを妨げる同期の代わりに、1つのオブジェクトをすべてのスレッドに割り当て、スレッドのスコープであるThreadLocalを維持します。スレッドセーフではないが最もよく使用されるオブジェクトの1つは、データベース接続とJMS接続です。

どうやって ?

1つの例は、SpringフレームワークがThreadLocalを頻繁に使用して、これらの接続オブジェクトをThreadLocal変数に保持することにより、背後でトランザクションを管理することです。高レベルでは、トランザクションが開始されると、接続が取得され(自動コミットが無効になります)、それがThreadLocalに保持されます。以降のdb呼び出しでは、同じ接続を使用してdbと通信します。最後に、ThreadLocalから接続を取得し、トランザクションをコミット(またはロールバック)して、接続を解放します。

log4jもMDCを維持するためにThreadLocalを使用すると思います。


5

ThreadLocal 異なるスレッド間で共有すべきではないが、存続期間中は各スレッドからアクセスできる状態にしたい場合に便利です。

例として、各リクエストが異なるスレッドによって処理されるWebアプリケーションを想像してください。リクエストごとに複数のデータが必要になると想像してください。これは計算に非常にコストがかかります。ただし、そのデータは着信要求ごとに変更されている可能性があります。つまり、プレーンキャッシュを使用できません。この問題の簡単で迅速な解決策は、ThreadLocalこのデータへのアクセスを保持する変数を用意することです。これにより、要求ごとに1回だけ計算する必要があります。もちろん、この問題はを使用しなくても解決できますがThreadLocal、説明のために考案しました。

とは言っても、ThreadLocalsは本質的にグローバルな状態の一種であることを覚えておいてください。その結果、他の多くの影響があり、他のすべての可能なソリューションを検討した後でのみ使用する必要があります。


ThreadLocalsは、グローバル状態にしない限り、グローバル状態ではありません。それらは事実上常にアクセス可能なスタックリソースです。すべてのメソッドで(遅延される)変数を渡すだけで、スレッドローカルをシミュレートできます。それはそれをグローバルな状態にすることはありません...
Enerccio '20

コード内のどこからでも(同じスレッドのコンテキスト内で)アクセスできるという意味で、これはグローバル状態の形式です。これには、誰がこの値を読み書きするかについて推論できないなど、すべての影響が伴います。関数パラメーターを使用することは遅延するものではなく、クリーンなインターフェースを促進する良い習慣です。ただし、コードベースの深さ全体にパラメーターを渡すことはコードのにおいであることに同意します。しかし、多くの場合、ThreadLocalの使用は最初にコードのにおいであり、ここであなたを導いたので、これを再考する必要があります。
DIMOS

単にオブジェクトの状態の一部にすることができ、グローバルに使用する必要はありません。確かに少しオーバーヘッドが発生しますが、複数のオブジェクトがスレッドごとに異なる状態を持つ必要がある場合はThreadLocal、オブジェクトフィールドとして使用できます...
Enerccio

あなたが正しい。ThreadLocal世界中でアクセス可能なのいくつかの誤用に遭遇した可能性があります。あなたが言ったように、それはまだ可視性を制限するクラスフィールドとして使用できます。
Dimos

4

ここでは特に新しいことはありませんが、今日ThreadLocal、WebアプリケーションでBean Validationを使用するときに非常に役立つことがわかりました。検証メッセージはローカライズされていますが、デフォルトではを使用しますLocale.getDefault()。をValidator別のMessageInterpolatorで構成できますが、Locale呼び出し時にを指定する方法はありませんvalidate。そのため、静的ThreadLocal<Locale>(または、より良いのは、必要になる可能性のある他のものを備えた一般的なコンテナー)をThreadLocal作成しMessageInterpolatorLocaleそこからカスタムを選択するようにすることです。次のステップはServletFilter、セッション値を使用するを書き込むかrequest.getLocale()、ロケールを選択して保存することですThreadLocal参考にしてください。


4

@unknown(google)で述べたように、その使用法は、参照される値が各スレッドで一意であるグローバル変数を定義することです。これは通常、現在の実行スレッドにリンクされているある種のコンテキスト情報を格納することを伴います。

これをJava EE環境で使用して、Java EE対応でないクラスにユーザーIDを渡します(HttpSessionまたはEJB SessionContextにアクセスできません)。このように、セキュリティベースの操作にIDを使用するコードは、すべてのメソッド呼び出しで明示的に渡す必要なく、どこからでもIDにアクセスできます。

ほとんどのJava EE呼び出しでの操作の要求/応答サイクルは、ThreadLocalを設定および設定解除するための明確に定義された入口および出口ポイントを提供するため、このタイプの使用法を簡単にします。


4

ThreadLocalは、非同期メソッドの複数のスレッドによる可変オブジェクトへのアクセスが確実に同期されるようにします。つまり、可変オブジェクトをメソッド内で不変にします。

これは、スレッドごとにアクセス可能な可変オブジェクトの新しいインスタンスを与えることによって実現されます。つまり、各スレッドへのローカルコピーです。これは、ローカル変数のようにアクセスされるメソッドのインスタンス変数を作成するためのハックです。ご存じのとおり、メソッドのローカル変数はスレッドでのみ使用できますが、1つの違いがあります。メソッドローカル変数は、メソッドの実行が終了するとスレッドで使用できなくなります。スレッドローカルで共有される可変オブジェクトは、クリーンアップするまで複数のメソッドで使用できます。

定義により:

JavaのThreadLocalクラスを使用すると、同じスレッドでのみ読み書きできる変数を作成できます。したがって、2つのスレッドが同じコードを実行していて、コードにThreadLocal変数への参照がある場合でも、2つのスレッドは互いのThreadLocal変数を参照できません。

ThreadJavaのそれぞれに含まThreadLocalMapれています。
どこ

Key = One ThreadLocal object shared across threads.
value = Mutable object which has to be used synchronously, this will be instantiated for each thread.

ThreadLocalの達成:

次に、以下のような(の有無にかかわらずinitialValue())可変オブジェクトを保持するThreadLocalのラッパークラスを作成します。
このラッパーのゲッターとセッターは、可変オブジェクトではなくスレッドローカルインスタンスで動作します。

threadlocalのgetter()がのthreadlocalmapで値を検出しなかった場合Thread。次に、initialValue()を呼び出して、スレッドに関するプライベートコピーを取得します。

class SimpleDateFormatInstancePerThread {

    private static final ThreadLocal<SimpleDateFormat> dateFormatHolder = new ThreadLocal<SimpleDateFormat>() {

        @Override
        protected SimpleDateFormat initialValue() {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd") {
                UUID id = UUID.randomUUID();
                @Override
                public String toString() {
                    return id.toString();
                };
            };
            System.out.println("Creating SimpleDateFormat instance " + dateFormat +" for Thread : " + Thread.currentThread().getName());
            return dateFormat;
        }
    };

    /*
     * Every time there is a call for DateFormat, ThreadLocal will return calling
     * Thread's copy of SimpleDateFormat
     */
    public static DateFormat getDateFormatter() {
        return dateFormatHolder.get();
    }

    public static void cleanup() {
        dateFormatHolder.remove();
    }
}

wrapper.getDateFormatter()呼ぶthreadlocal.get()と、それはチェックしますcurrentThread.threadLocalMapが含まれ、この(ThreadLocalの)インスタンスを。
はいの場合、対応するスレッドローカルインスタンスの値(SimpleDateFormat)を返します。
それ以外の場合は、このスレッドローカルインスタンス、initialValue()でマップを追加します。

これにより、この変更可能なクラスでスレッドセーフが達成されます。各スレッドによって、独自の変更可能なインスタンスを使用していますが、同じThreadLocalインスタンスを使用しています。すべてのスレッドは同じThreadLocalインスタンスをキーとして共有しますが、値として異なるSimpleDateFormatインスタンスを共有します。

https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java


3

スレッドローカル変数は、変更可能なシングルトンまたはグローバル変数に基づく設計での共有を防ぐためによく使用されます。

これは、接続プールを使用していないときに、スレッドごとに個別のJDBC接続を作成するようなシナリオで使用できます。

private static ThreadLocal<Connection> connectionHolder
           = new ThreadLocal<Connection>() {
      public Connection initialValue() {
           return DriverManager.getConnection(DB_URL);
          }
     };

public static Connection getConnection() {
      return connectionHolder.get();
} 

getConnectionを呼び出すと、そのスレッドに関連付けられた接続が返されます。dateformatやスレッド間で共有したくないトランザクションコンテキストなどの他のプロパティでも同じことができます。

同じためにローカル変数を使用することもできますが、これらのリソースは通常、作成に時間がかかるため、それらを使用してビジネスロジックを実行するたびに何度も作成する必要はありません。ただし、ThreadLocal値はスレッドオブジェクト自体に格納され、スレッドがガベージコレクションされるとすぐに、これらの値も失われます。

このリンクは、ThreadLocalの使い方を非常によく説明しています。


2
この例では、主要な問題です。接続を閉じる責任は誰にありますか?この接続を初期値として作成する代わりに、接続のコンシューマに接続を明示的に作成させ、ThreadLocalを介してスレッドにバインドさせます。接続の作成者は、終了する責任もあります。これはこの例では明確ではありません。接続の作成とバインドは、Transaction.begin()やTransaction.end()などの単純なフレームワークでも非表示にできます。
Rick-Rainer Ludwig

2

Java のThreadLocalクラスを使用すると、同じスレッドだけが読み書きできる変数を作成できます。したがって、2つのスレッドが同じコードを実行していて、コードにThreadLocal変数への参照がある場合でも、2つのスレッドは互いのThreadLocal変数を参照できません。

続きを読む


2

使用するための3つのシナリオがあり、クラスのヘルパーを最良のものが使用されるマルチスレッドコードでのSimpleDateFormat、同じようにThreadLocalの

シナリオ

1-アプリを遅くするロックまたは同期メカニズムの助けを借りて、同様の共有オブジェクトを使用する

2-メソッド内でローカルオブジェクトとして使用する

このシナリオでは、4つのスレッドがあり、それぞれがメソッドを1000呼び出す場合、
4000の SimpleDateFormat オブジェクトが作成され、GCがそれらを消去するのを待機しています。

3- ThreadLocalの使用

私たちは4スレッドを持っている場合、我々はに与えた各スレッド1つのSimpleDateFormatのインスタンスを
私たちは持っているので、4つのスレッド4つのオブジェクトのSimpleDateFormatのを。

ロックメカニズムやオブジェクトの作成と破棄は必要ありません。(良い時間の複雑さと空間の複雑さ)


2

[参考] ThreadLocalでは、共有オブジェクトの更新に関する問題を解決できません。同じスレッド内のすべての操作で共有されるstaticThreadLocalオブジェクトを使用することをお勧めします。[必須] remove()メソッドは、特にスレッドが頻繁に再利用されるスレッドプールを使用する場合は、ThreadLocal変数によって実装する必要があります。そうしないと、後続のビジネスロジックに影響を及ぼし、メモリリークなどの予期しない問題を引き起こす可能性があります。


1

キャッシュ、同じ値を何度も計算する必要がある場合があるため、メソッドへの入力の最後のセットと結果を格納することにより、コードを高速化できます。スレッドローカルストレージを使用すると、ロックについて考える必要がなくなります。


1

ThreadLocalは、JVMによって特別にプロビジョニングされる機能であり、スレッドのみに分離されたストレージスペースを提供します。インスタンススコープ変数の値のように、クラスの特定のインスタンスのみにバインドされます。各オブジェクトには唯一の値があり、他の各値を見ることができません。ThreadLocal変数の概念もそうです。それらは、オブジェクトインスタンスの意味でスレッドに対してローカルです。それを作成したスレッドを除いて、他のスレッドはそれを見ることができません。こちらをご覧ください

import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;


public class ThreadId {
private static final AtomicInteger nextId = new AtomicInteger(1000);

// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId = ThreadLocal.withInitial(() -> nextId.getAndIncrement());


// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
    return threadId.get();
}

public static void main(String[] args) {

    new Thread(() -> IntStream.range(1, 3).forEach(i -> {
        System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
    })).start();

    new Thread(() -> IntStream.range(1, 3).forEach(i -> {
        System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
    })).start();

    new Thread(() -> IntStream.range(1, 3).forEach(i -> {
        System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
    })).start();

}
}

1

Threadlocalは、ゼロのコストでオブジェクトの再利用を実現する非常に簡単な方法を提供します。

各更新通知で、複数のスレッドが可変キャッシュのイメージを作成している状況がありました。

各スレッドでThreadlocalを使用したので、各スレッドは古いイメージをリセットし、各更新通知でキャッシュから再度更新するだけで済みます。

オブジェクトプールからの通常の再利用可能なオブジェクトには、スレッドセーフのコストが関連付けられていますが、このアプローチにはありません。


0

次の小さな例を試して、ThreadLocal変数の感触をつかんでください。

public class Book implements Runnable {
    private static final ThreadLocal<List<String>> WORDS = ThreadLocal.withInitial(ArrayList::new);

    private final String bookName; // It is also the thread's name
    private final List<String> words;


    public Book(String bookName, List<String> words) {
        this.bookName = bookName;
        this.words = Collections.unmodifiableList(words);
    }

    public void run() {
        WORDS.get().addAll(words);
        System.out.printf("Result %s: '%s'.%n", bookName, String.join(", ", WORDS.get()));
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(new Book("BookA", Arrays.asList("wordA1", "wordA2", "wordA3")));
        Thread t2 = new Thread(new Book("BookB", Arrays.asList("wordB1", "wordB2")));
        t1.start();
        t2.start();
    }
}


スレッドBookAが最初に実行された場合のコンソール出力:
結果BookA: 'wordA1、wordA2、wordA3'。
結果BookB: 'wordB1、wordB2'。

スレッドBookBが最初に実行された場合のコンソール出力:
結果BookB: 'wordB1、wordB2'。
結果BookA: 'wordA1、wordA2、wordA3'。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.