メモリリークのThreadLocalで詳細な分析

  この研究を通じて、これらの問題を解決することを望んで最初の一般的な質問の下にThreadLocalに記載されている古いルーチンは、ノート:
  
  ThreadLocalのはどんな問題を解決するのですか?
  
  どのようにThreadLocalを使用するには?
  
  何であるかThreadLocalの原則?
  
  ThreadLocalのを使用して、実際のプロジェクトのいくつかの例をできますか?
  
  基本
  
  ThreadLocalのスレッドローカル変数、及び異なる共通変数は、各スレッドには、この変数のコピーを保持する(設定方法)とアクセス(getメソッド)この変数の、スレッド間には競合独立して変更することができます。
  
  一般的に定義されたクラスのThreadLocalのインスタンスは、通常、いくつかのビジネスのThreadLocalパッケージID(ユーザーIDやトランザクションID)で、ビジネス、状態のThreadLocalのインスタンスを許可し、結合スレッドうプライベート静的に、変更されます-別のスレッドを使用しますIDは同じではありません。
  
  使い方
  
  ケース1の
  
  ビューの特定のポイントから、ThreadLocalのはJavaで並列プログラミングのための追加的なアイデアを提供-などのSimpleDateFormat、あなたのように、オブジェクト自体はスレッドセーフではありませんが、マルチスレッド同期アクセスの効果を達成する必要がある場合は、同時避けてくださいあなたはThreadLocal変数を使用することができます。
  
  クラスFooのは、パブリック
  
  {
  
  // SimpleDateFormatのはSO一つは、各スレッドを与えるために、スレッドセーフではありません
  
  最終ThreadLocalの静的<てSimpleDateFormat> =新しい新しいフォーマッタのThreadLocal <てSimpleDateFormat>(){プライベート
  
  @Overrideが
  
  てSimpleDateFormatはinitialValue(保護)
  
  {
  
  新しい新しいてSimpleDateFormat( "YYYYMMDD HHMM")を返す;
  
  }
  
  };
  
  パブリック文字列formatIt(日付)
  
  {
  
  戻りformatter.get ().format(DATE);
  
  }
  
  }
  
  これが唯一のカスタム・スレッドのSimpleDateFormatメンバ変数の定義と、実際には、各スレッドのオブジェクトのSimpleDateFormat一度初期化する必要があり、プロセスが新しいオブジェクトを初期化するとき、効果は同じであることに注意してくださいが、これはより多くの定期的なコードです。
  
  ケース2の
  
  yunosは、データ移行プロジェクトのためにディスクを冷却する前に、我々は寸法をロックするユーザーを追跡するために必要な時間、移行プロセスの前に各スレッドは、現在のユーザーのロックを取得する必要があり、各キーには、ユーザー情報を使用することですそれはまた、ThreadLocalの変数を使用して達成することができる。image.png
  
  ケース3を
  
  例以下、我々はMyRunnableオブジェクトを定義し、このオブジェクトは、整数への各スレッドアクセス糸1及び2のスレッドを使用してMyRunnableであるが、内部ThreadLocal変数によって彼らは自分の別のコピーです。
  
  パッケージorg.java.learn.concurrent.threadlocal。
  
  / **
  
  * @author duqi
  
  * @createTime 2018年12月29日午後11時25
  
  ** /
  
  パブリッククラスThreadLocalExample {
  
  パブリック静的クラスMyRunnableは実装のRunnable {
  
  プライベートThreadLocalの<整数> THREADLOCAL =
  
  新しいThreadLocalの<整数>();
  
  @Override
  
  ます。public void実行(){
  
  threadLocal.set((int型)(Math.random()* 100D));
  
  {試す
  
  のThread.sleep(2000);
  
  }キャッチ(InterruptedExceptionある電子){
  
  }
  
  のSystem.out.println(threadLocal.get())。
  
  }
  
  }
  
  公共の静的な無効メイン(文字列[]引数)InterruptedExceptionある{スロー
  
  MyRunnable sharedRunnableInstance =新しいMyRunnableを();
  
  =新しい新しいスレッド1スレッドスレッド(sharedRunnableInstance);
  
  スレッド=新しい新しいスレッド2スレッド(sharedRunnableInstance);
  
  thread1.start();
  
  thread2.start();
  
  thread1.join(www.jinmagjt.com); //待機のために1スレッド終了する
  
  スレッド2を.join(); //終了するスレッド2待ち
  
  }
  
  }
  
  ThreadLocalの鍵知識
  
  ソースコード解析
  
  スレッドのThreadLocalは、それが使用される方法は?以下に示すような原理:スタック上のThreadLocalの参照と参照のスレッドは、マップThreadLocalのオブジェクト(弱い参照パッケージを使用)でのキーであるスレッドオブジェクト、ThreadLocalMap参照を参照し、サービス価値は、変数の値です。image.png
  
  java.lang.Threadの内のコードで最初に見て:
  
  パブリック
  
  クラス実装しているスレッドのRunnable {
  
  // ...他のソース
  
  このスレッドに関係する/ * ThreadLocalの値は、このマップはThreadLocalのクラスによって維持されています* / ..
  
  ThreadLocals = nullをThreadLocal.ThreadLocalMap;
  
  / *
  
  *これはInheritableThreadLocalクラスで地図いるスレッドによって維持さこれに関連InheritableThreadLocal値。。
  
  * /
  
  ThreadLocal.ThreadLocalMap inheritableThreadLocals = NULL;
  
  // ......他のソース
  
  変数スレッドのthreadLocalsマップを指し、マップが結合現在のスレッドにThreadLocal変数に格納されThreadLocal.ThreadLocalMap、であり; inheritableThreadLocalsもThreadLocal変数に格納されているが、ストアが親から継承されているのと同じ効果は、現在のスレッドをスレッドThreadLocal変数。
  
  java.lang.ThreadLocalクラスを探して、以下のようにインターフェースの主メンバーは、次のとおりimage.png
  
  withInitial方法を、変数はサプライヤによって決定されるであろうThreadLocalの8外部呼び出しを得るために()メソッドの後のJava初期化方法初期値は、
  
  パブリック静的<S>のThreadLocal <S> withInitial(<?S延び>サプライヤサプライヤ){
  
  新しい新しいSuppliedThreadLocal <www.leyouzaixan.cn>(サプライヤー)を返します。
  
  }
  
  変数の現在のスレッドさらなるコピーが作成されていない場合、はinitialValueメソッドを呼び出して初期値を設定する必要がある、可変現在のスレッドのコピーを得る方法を取得またはメソッドのソースコードを取得し、最初の現在のスレッドによって地図に対応する現在のスレッドを取得します、マップエントリが除去され、その後、対応する値が取らに対応するマップから、空でない場合、マップが空の場合、初期値はsetInitialValue呼を設定し、マップが空でない場合、エントリThreadLocalのインスタンスに対応する電流が空です、また、初期値を設定します。
  
  T)は(公共GET {
  
  スレッドにThread.currentThread = T();
  
  ThreadLocalMap GetMapリクエスト地図=(T);
  
  IF(地図= nullを!){
  
  ThreadLocalMap.Entry map.getEntry E =(この);
  
  IF(!= nullのE) {
  
  @SuppressWarnings( "未チェック" www.tdcqpt.cn)
  
  T結果=(T)e.Value;
  
  リターン結果;
  
  }
  
  }
  
  setInitialValue()が返す;
  
  }
  
  SETメソッドは、GETメソッドと同様に、現在のスレッドが対応するマップを取得するために、マップが空の場合、呼び出しは、マップへの変数のそれ以外の値をマップを作成createMap -現在のThreadLocalオブジェクトへの鍵は、値が変数の値です。
  
  公共ボイド集合(T値){
  
  T =にThread.currentThreadスレッド();
  
  ThreadLocalMap地図= GetMapリクエスト(T);
  
  IF(地図= nullを!)
  
  Map.set(この、値);
  
  他の
  
  CreateMap(T、値);
  
  }
  
  バインディング現在のスレッドを削除するには、Removeメソッドこのコピー
  
  公共ボイド削除(www.zzxcscl.com){
  
  ; ThreadLocalMap GetMapリクエストM =(にThread.currentThread())
  
  IF(!www.kunLunyuLegw.com M = NULL)
  
  m.remove(この);
  
  }
  
  デジタル0x61c88647、この値がHASH_INCREMENTの値であり、一般的なハッシュマップは、競合のリストを処理するために使用されるが、ThreadLocalMap競合を検出するための線形方法を使用して、HASH_INCREMENTは参照1、この番号はに選択されることを特徴によれば、たびにステップサイズを増加させます最小の競合の可能性を聞かせて。
  
  / **
  
  * -difference BETWEEN次々ハッシュコードで生成された-になります
  
  *スプレッドでほぼ最適INTO暗黙のシーケンシャルスレッドローカルID
  
  電源のための乗法ハッシュ値* -of-TWO-サイズのテーブル。
  
  * /
  
  プライベート静的int型HASH_INCREMENT決勝= 0x61c88647、
  
  親と共有の子データ
  
  主に子スレッドを使用InheritableThreadLocalが作成され、あなたは自動的に達成するために、親スレッドのThreadLocal変数を継承する必要があり、サブスレッドのアクセスThreadLocal変数親スレッド。InheritableThreadLocalはThreadLocalをを継承し、childValue、GetMapリクエスト、createMap 3つのメソッドを書き換えます。
  
  InheritableThreadLocalクラス公開<T>拡張のThreadLocal <T> {
  
  / **
  
  *あなたが親スレッドThreadLocal変数を継承する必要がある場合や、あなたがコピーした親スレッドThreadLocal変数が必要になりますと、スレッドを作成します。
  
  * /
  
  保護childValue T(T http://www.wanxinyulept.com/chaoyue parentValue){
  
  parentValueを返す;
  
  }
  
  / **
  
  GetMapリクエストを書き換えるためだけクラススレッドを動作させるので、InheritableThreadLocal動作変数、* inheritableThreadLocals変数、threadLocals変数とは無関係
  
  ** /
  
  GetMapリクエストThreadLocalMap(スレッドT){
  
  t.inheritableThreadLocalsを返す;
  
  }
  
  / **
  
  のみinheritableThreadLocalsスレッドクラス変数で動作する、変数を設定するかgetInheritableThreadLocal、GetMapリクエストと類似*
  
  * /
  
  ボイドCreateMap(HTTPスレッド://www.zzhehongを.COM / Chaoyue /、T firstValue){
  
  t.inheritableThreadLocals =新新ThreadLocalMap(これ、firstValue);
  
  }
  
  }
  
  childValueには、いくつかの単語は、何が起こったかのコピーである、と言いますか?
  
  第一の方法、Thread.init見
  
  ます。private void(スレッドグループG、Runnableをターゲットの、文字列名、ロングSTACKSIZE、AccessControlContextにACC、ブールinheritThreadLocals)のinit {
  
  //他のソース
  
  (inheritThreadLocals parent.inheritableThreadLocals &&!= NULL)IF
  
  this.inheritableThreadLocals WWWを。 zbzxyL12.com =
  
  ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
  
  / *場合には指定されたスタッシュスタックサイズはVM * / CARES
  
  = this.stackSizeスタックサイズを;
  
  / * *セットIDスレッド/
  
  TID = nextThreadIDは();
  
  }
  
  この方法をThreadLocal.createInheritedMap見て、最終的には、書き換えを行うためにここにchildValue InheritableThreadLocalのnewThreadLocalMapメソッドに呼び出されます、我々はここで見ることができます実際にスレッドThreadLocalMap内の子にコピーされた順番に親スレッドThreadLocalMapに関連したコンテンツです。
  
  ThreadLocalMapプライベート(ThreadLocalMap parentMap){
  
  エントリー[] = ParentTable parentMap.table;
  
  INT LEN = parentTable.length;
  
  SetThreshold(LEN);
  
  表新しい新しい=エントリ[LEN];
  
  のための(INT J = 0; J <lenを、J ++){
  
  E = ParentTableエントリー[J];
  
  IF(!E = NULL){
  
  @SuppressWarnings( "未チェック")
  
  ThreadLocalの<OBJECT> =キー(ThreadLocalの<OBJECT>)e.get();
  
  IF(キー= NULL!){
  
  Object値= key.childValue(e.Value);
  
  エントリ新しい新しいCは、エントリ(キー、値)=;
  
  INT &key.threadLocalHashCode = H(LEN - 1);
  
  (!表[H] = NULL)、一方
  
  H = nextIndex(H、LEN);
  
  表[H] = C;
  
  サイズ++;
  
  }
  
  }
  
  }
  
  }
  
  ThreadLocalのオブジェクトが回収された場合?
  
  キーはThreadLocalのオブジェクト、次いでThreadLocalのオブジェクトが弱い参照に包装されているThreadLocalMap弱参照到達不能であると判定されたThreadLocalのオブジェクトまたはThreadLocalのオブジェクトのマップ後に強い基準点がない場合ように、ガベージコレクションはであろうそれはオフに回収しました。エントリ定義を見て:
  
  静的クラスのエントリを拡張弱い参照<ThreadLocalの<>> {?
  
  / **関連する値* /でこのThreadLocalを持つ。
  
  Object値。
  
  エントリ(ThreadLocalのK、Vオブジェクト<?>){
  
  スーパー(K);
  
  値= V;
  
  }
  
  }
  
  ThreadLocalのスレッドプールと一緒に使用?
  
  一緒に使用ThreadLocalのオブジェクトとスレッドプールは、あなたがこのような状況が発生する可能性がありますので、もしライフサイクルの糸のようなオブジェクトThreadLocalのライフサイクルは、長いです:ThreadLocalのオブジェクトは、スレッドは、一般的に、アウト文字列のオブジェクトや他のスレッドをしていないのThreadLocalますです一緒に両方を使用することをお勧めします。
  
  ケーススタディ
  
  のThreadLocalでダボの使用は、
  
  次のようにIは、主にリクエストキャッシュシーンに使用され、ダボThreadLocalの例から特定のコードを発見した:
  
  @Activate(グループ= {Constants.CONSUMER、Constants.PROVIDER}、値=定数.CACHE_KEY)
  
  パブリッククラスCacheFilter実装フィルタ{
  
  プライベートCacheFactoryのCacheFactoryの、
  
  公共ボイドsetCacheFactory(CacheFactoryのCacheFactoryの){
  
  this.cacheFactory = CacheFactoryの;
  
  }
  
  @Override
  
  公共の結果の呼び出し(実行者<?>呼び出し、呼び出しの呼び出しは)RpcException {スロー
  
  であれば(CacheFactoryの!= nullの&& ConfigUtils.isNotEmpty(invoker.getUrl()。getMethodParameter(invocation.getMethodName()、Constants.CACHE_KEY))){
  
  キャッシュのキャッシュを= cacheFactory.getCache(invoker.getUrl()呼び出し)。
  
  (もし!キャッシュ= NULL){
  
  文字列のキー= StringUtils.toArgumentString(invocation.getArguments());
  
  オブジェクト値= cache.get(キー)。
  
  もし(!値= NULL){
  
  (ValueWrapper instanceofは値)であれば、{
  
  新しいRpcResultを返します(((ValueWrapper)値)に.get());
  
  }他{
  
  新しいRpcResult(値)を返します。
  
  }
  
  }
  
  結果結果= invoker.invoke(呼び出し)。
  
  (もし!result.hasException()){
  
  cache.put(キー、新しい新ValueWrapper(また、Result.getValue()));
  
  }
  
  リターン結果;
  
  }
  
  }
  
  戻りinvoker.invoke(呼び出し)は、
  
  }
  
  RPCコールで見ることができます(起動)リンク、意志最初の使用この呼び出しはThreadLocalCacheが保存されて使用されます-現在のスレッドがちょうど同じ引数を呼び出すことによって開始されているかどうかを判断するリクエストパラメータ。次のように具体的に参照し、ThreadLocalCacheが実装:
  
  パッケージorg.apache.dubbo.cache.support.threadlocal;
  
  インポートorg.apache.dubbo.cache.Cache;
  
  インポートorg.apache.dubbo.common.URL、
  
  インポートたjava.util.HashMap ;
  
  インポートjava.util.Map;
  
  / **
  
  * ThreadLocalCache
  
  * /
  
  publicクラスThreadLocalCache実装キャッシュ{
  
  // ThreadLocalのはのマッピング結果パラメータに格納された
  
  民間最終のThreadLocal <マップ<オブジェクト、オブジェクト>>店。
  
  (URLとURL)ThreadLocalCache公共{
  
  this.store ThreadLocalの新しい新しい= <地図<オブジェクト、オブジェクト>>(){
  
  @Overrideは
  
  地図を保護<オブジェクト、オブジェクト>はinitialValue(){
  
  新しい新規のHashMap <オブジェクトを返す、オブジェクト>();
  
  }
  
  } ;
  
  }
  
  @Override
  
  公共ボイドPUT(オブジェクトキー、オブジェクト値){
  
  store.get()PUT(キー、値);.
  
  }
  
  @Override
  
  公共GETオブジェクト(オブジェクトキー){
  
  戻りstore.get()(キー)を得ます。 ;
  
  }
  
  }
  
  RocketMQ
  
  RocketMQでもThreadLocalの数字を発見I、それが送信されたシーンのメッセージで使用される特定のキューを選択するために必要なステップ、選択されたがあり、MQClientAPIImplは、サービスの終了を実装するためにメッセージを送信するRMQ責任があります特定のキューには、別のスレッドは、ここで使用し、独自のインデックス値責任ThreadLocalの機構を備えているとき、ThreadLocalIndexの達成するために見ることができます。
  
  org.apache.rocketmq.client.commonのパッケージペナルティを。
  
  輸入java.util.Randomの。
  
  パブリッククラスThreadLocalIndex {
  
  民間最終ThreadLocalの<整数> threadLocalIndex =新しいThreadLocalの<整数>();
  
  民間最終ランダムランダム=新しいランダム();
  
  公共INT getAndIncrement(){
  
  整数インデックス= this.threadLocalIndex.get()。
  
  IF(ヌル==インデックス){
  
  インデックス= Math.abs(にRandom.nextInt())。
  
  (インデックス<0)であれば
  
  、インデックス= 0。
  
  this.threadLocalIndex.set(インデックス)。
  
  }
  
  インデックス= Math.abs(インデックス+ 1)。
  
  (インデックス<0)であれば
  
  、インデックス= 0。
  
  this.threadLocalIndex.set(インデックス)。
  
  インデックスを返します。
  
  }
  
  @Override
  
  パブリック文字列のtoString(){
  
  リターン"ThreadLocalIndex {" +
  
  "threadLocalIndex =" + threadLocalIndex.get()+
  
  '}';
  
  }
  
  }
  
  まとめ
  
  この記事は、ThreadLocalのに関するいくつかの問題が解決されるには:(1)特定のコンセプトは何ですか?(2)どのようなシーンの開発でJavaを使用?原則(3)にThreadLocalは似ているのですか?(4)あなたはを参照することができ、その場合には、オープンソース・プロジェクトを?私はこれらのいくつかの質問は、あなたがそれのいくつかを理解しているかどうかわからないのですか?質問があれば、交換してください。
  
  参考文献
  
  なぜ0x61c88647?
  
  のJavaのThreadLocal
  
  とどのように私はThreadLocal変数使用する必要がありますか?
  
  に理解JavaのThreadLocalの:技術暗い部屋を
  
  ThreadLocalのメモリリークの-depth分析
  
  「戦闘でのJava並行処理」
  
  InheritableThreadLocal詳細
  
  ThreadLocalの詳しい
  
  使用ThreadLocalのシーン
  
  データ構造:ハッシュテーブル

おすすめ

転載: www.cnblogs.com/qwangxiao/p/11222917.html