ThreadLocalはインタビューに対応するだけでなく、本当に理解して実際に使用する必要があります

序文

数年前に初めてインタビューを受けたとき、私はこれについて尋ねられたことを覚えています。インタビュアーは、直接ThreadLocalの実装原理について、またそれが使用された場所を見たことがあるかどうかについて尋ねるように頼んだことを覚えています。
初めて就職したときはまだ新人だったので、すぐに騙されて、今でも新鮮な思い出があります。

これをまとめても大丈夫です。実際、このThreadLocalについては慎重に検討してください。全体的なアイデアは実際には非常に明確ですが、一部の詳細は難しく、通常は使用されない深い知識が必要になる場合があります。正直なところ、私は知りません完全に明確ですが、常に努力しています。

コンセプト

定義

ThreadLocalはjava.langパッケージの下のクラスであり、このクラスは特別なスレッドローカル変数を提供するため、変数にアクセスする各スレッドは内部に初期化変数の独立したコピーを持っています。
人間の言葉で説明すると
、通常のクラスで定義された変数についてお話ししましょう。複数のスレッドで共有されていることは誰でも知っています。
ThreadLocalクラスには、さまざまなスレッドに固有の特別な変数があります。このThreadLocalを使用すると、このスレッドの一意の値を取得できます。設定および取得でき、スレッドは相互に排他的ではありません。影響。

実際、ThreadLocalの概念はJava言語に固有のものではありません。実際、多くの言語にこの概念がありますが、Javaでは、この概念はハッシュテーブルで実装されます。

特徴

シンプルでオーバーヘッドが少なく、スレッドセーフです。

ThreadLocalを使用する場所

1. QuartzのSimpleSemaphoreはリソースの分離を提供します

ここに画像の説明を挿入
ここに画像の説明を挿入
上の画像を
見てくださいSimpleSemaphoreにメソッドがあります:同期ロックを使用するobtrainLockメソッド
です。このメソッドには重いwhile操作があります(コンシューマーはすべてのものを処理し、新しいものを待つ必要があります。この待機はwhileループです)
lockName isこのメソッドの入力パラメーター、このwhileメソッドの判断ロジックは、HashSetのlockNameにこのlockNameがある場合、obtrainLock自体がメソッドであるため、このスレッドがwait()メソッドを実行し、次にスレッドを待機()することです。それは完全にブロックされ、ここでキューに入れられました。
最初にThreadLocalフィルターがない場合、同じスレッドが同じlockNameでobtrainLockメソッドを複数回呼び出し、whileループに複数回入ることを想像してください。実際、同じスレッドがこの操作に複数回入る必要がない
ためこのロック操作の前に、ThreadLocal判定(isLockOwnerメソッド)を使用して、同じスレッドが同じlockNameを使用してこのメ​​ソッドを呼び出す回数を1回に減らします。つまり、whileループに初めて入るだけで、もう1つはisLockOwnerメソッドによってロックされます。
最終的にブロックされると、アクセスの背後にある重い操作の頻度が大幅に減少します。これは最適化です。

2. MybatisのSqlSessionManager、リソース保持

Mybatisがデータベースに接続した後、複数の接続を維持する接続プールが存在することがわかります。データベースを操作するたびに、接続を取得して操作に移動する必要があります。接続はsqlSession.getConnectionメソッドであり、すべての操作が可能です1つの接続を取得します。
トランザクションをサポートする場合は、トランザクションのすべての操作を同じ接続で処理できるようにする必要があります。これにより、操作が成功または失敗し、トランザクションの各操作がスレッドプールから接続を取得する必要があります。トランザクションのすべての操作が同じ接続を確実に取得する方法は?
トランザクションの複数の操作は通常1つのスレッドによって実行されますが、実際には、スレッドが常に同じ接続を取得する方法が問題になります。ここでThreadLocalは、現在のスレッドによって取得された接続をThreadLocalでは、スレッドが次に接続を取得するときに、ThreadLocalから直接接続を取得します。これにより、同じスレッドが常に同じ接続を取得し、他のスレッドが実行する接続がこのスレッドの影響を受けなくなります。

特定のコード実装を見てみましょう。
最初にThreadLocalを定義し、SqlSessionを保存します。各接続はSqlSessionに対応し、
ここに画像の説明を挿入
次に
ここに画像の説明を挿入
コミット、ロールバック、その他のメソッドなどの実際の使用のために、スレッドのSqlSessionをThreadLocalに配置します。接続しました。
ここに画像の説明を挿入

3、Spring的TransactionContextHolder

ここに画像の説明を挿入
TransactionContextは、分散トランザクションリソースプールとも呼ばれ、現在の環境のコンテキストを保存します。PlatformTransactionManagerがあります。これは、コミットとロールバックを実行するクラスです。またはロールバックして、結果としてTransactionContextをThreadLocalで保存して、効果を達成します。

4.ログイン

ログインするとき、各スレッドのログイン情報をThreadLocalに入れて、同じ人物の操作が常に同じスレッドで行われるようにすることができます。

ThreadLocalコアソースコードの解釈

1.まず、各スレッドにメンバー変数threadLocalsがあります。

これはThreadLocalに特別に追加されます。特定のthreadLocals割り当てプロセスは、ThreadLocal内
のthreadLocalsのタイプがThreadLocal.ThreadLocalMapであり、このThreadLocalMapはThreadLocalのカスタム内部マップクラス、キーはThreadLocalオブジェクト、値は各スレッドです変数の一意のコピー。
ここに画像の説明を挿入

2. ThreadLocalのgetメソッド

ここに画像の説明を挿入
まず、現在のスレッドの
getMapメソッドを取得します。つまり、現在のスレッドからThreadLocalMapを取得します。これは、Threadのメンバー変数です。
ThreadLocalMapのキーは現在のThreadLocalオブジェクトであり、値はgetメソッドが実際に返したい値です。
ThreadLocalMapを取得できる場合は、ThreadLocalMapの現在のThreadLocalオブジェクトに対応する値を返します。
ThreadLocalMapを取得できない場合は、値を初期化し、最後に値を返します。

まとめ

ThreadLocalの実装を見ると、ThreadLocalが異なるスレッドの異なる値を保存できる理由が明確にわかります。
実際、これらの値は依然として各スレッドのマップに格納されており、ThreadLocalはこのマップのキーにすぎません。
したがって、スレッドの場合、彼が複数のThreadLocalに遭遇すると、実際には、スレッド内のマップには多くの値のペアがあります。
逆の操作感はありますか?一見、これらの値はThreadLocalに保存されていると思いましたが、ようやくスレッドに保存されていることに気付きました。

注目

各スレッドのThreadLocalMapは、ThreadLocalで定義された静的クラスであり、マップを書き換えるThreadLocalと同等であることに注意してください。

実際、これはjavaガベージコレクションに関連する問題であり、書き直されたThreadLocalMapは主にこの問題のためのものです。

HashMapの実際のデータはEntryにあります。実際、ThreadLocalMapは同じですが、ThreadLocalMapのEntryはWeakReferenceクラスを継承しています。ThreadLocalMapのキー値は実際にはThreadLocalオブジェクトであることがわかっています。オブジェクトを設定するときは、このオブジェクトのハッシュ値に従ってハッシュテーブルでスロットを見つける必要があります。対応するスロットが見つかった場合、スロット上の元のオブジェクトがリサイクルされます、ハッシュテーブル上の位置の値がnullの場合、ThreadLocalMapは破棄されたnull値に対応するスロットに対して何らかの処理を行います(主にこれらのスロットを再利用し、ハッシュテーブルのサイズを再割り当てするなど)。これは、ガベージコレクションの結果を同期することと同じです。

これが、hashMapがこれらのロジックを処理しないため、hashMapを書き換える必要がある理由です。hashMapが処理されない場合、スロットがリサイクルされ、解放できないThreadLocalの空のオブジェクトによって継続的に占有され、時間がかかるため、最終的にハッシュ検索に影響します。各通常のハッシュの後に配置する必要があるスロットはnullで占められており、後方にのみ移動できます。

元の記事203件を公開 賞賛された186件 210,000回

おすすめ

転載: blog.csdn.net/java_zhangshuai/article/details/105462391