最近、Android Framework Layerコードを読んで、Threadlocalクラスを見ました。私は少し馴染みがなかったので、さまざまな関連するブログを1つずつ読みました。その後、ソースコードを調査し、以前に読んだブログの投稿とは理解が異なることがわかりました。そのため、次の役割を果たすことができることを期待して、自分の理解について話すために記事を書くことにしました。
- 研究結果をクリアし、理解を深めることができます。
- それはジェイドを引き付ける上で役割を果たし、興味のある学生が自分のアイデアをクリアするのを助けることができます。
- 学習体験を共有し、すべての人とコミュニケーションを取り、学びます。
1。threadlocalとは何ですか
Threadlocalは、Java.langの下にあるJavaクラスライブラリの基本クラスです。
公式の説明は次のとおりです。
スレッドローカルストレージ、つまり、各スレッドに独自の値がある変数を実装します。すべてのスレッドは同じThreadlocalオブジェクトを共有しますが、それぞれにアクセスすると異なる値が表示され、1つのスレッドによって行われた変更は他のスレッドに影響しません。実装はnull値をサポートします。
一般的な意味は次のとおりです。
スレッドのローカルストレージメカニズムを実装できます。 threadlocal変数は、異なるスレッドで異なる値を持つことができる変数です。すべてのスレッドは同じThreadlocalオブジェクトを共有できますが、異なるスレッドはアクセスすると異なる値を取得でき、それに対するスレッドの変更は他のスレッドに影響しません。クラスの実装は、null値をサポートします(null値に渡してセットでアクセスしてメソッドを取得できます)。
要約すると、3つの特性があります。
- 異なるスレッドでアクセスすると、異なる値を取得します
- それへのスレッドの変更は他のスレッドに影響しません
- サポートnull
以下は、これらの機能の例です。まず、テストクラスを定義します。このクラスでは、上記の3つの機能を確認します。クラスの定義は次のとおりです。
test.java
public class test {// threadlocal private static threadlocal name; public static void main(string [] args)throws exception {name = new threadlocal(); // define thread athread a = new shood(){public.out.println(」 invoke set、valueは: "+name.get());}}セットを呼び出し、値を印刷しますnullsystem.out.println(name.get()); // valueName.set( "thread main"); // starch aa.start(); a.join(); //スレッドasystem.out.out.out.println(name.get());/ starch swreet swreat swreat swreat swreat swreat swreat swreat swret swreat swreat swreat swreat swreat swreat swreat swreat swreat swreat swreat swreat swreat swreat swreat swreat swreat aibs();スレッドBsystem.out.println(name.get())で値を変更しました}}コード分析:
定義から、1つのThreadlocalオブジェクトのみが宣言され、他の3つのスレッド(メインスレッド、スレッドA、スレッドB)が同じオブジェクトを共有することがわかります。次に、オブジェクトの値が異なるスレッドで変更され、オブジェクトの値は異なるスレッドでアクセスされ、結果はコンソールで表示する出力です。
結果を参照してください:
コンソール出力の結果から、3つのヌル出力があることがわかります。これは、オブジェクトが出力の前に割り当てられなかったためであり、nullをサポートする機能を検証したためです。さらに、各スレッドではオブジェクトの値を変更しましたが、他のスレッドがオブジェクトにアクセスすると、それは変更された値ではなく、スレッドローカル値です。これにより、他の2つの機能も検証されます。
2。threadlocalの役割
誰もが、その使用シナリオがほとんどマルチスレッドプログラミングであることを知っています。その特定の機能については、それをどう思いますか?これは一般的な方法でのみ定義できると思います。なぜなら、物の機能的属性は、定義された後にすべての人の思考を制限するからです。たとえば、包丁は野菜を切るために使用され、多くの人はそれを使用してスイカを切ることはありません。
ここでは、参照のためだけにその機能についての私の理解について話し、それが役立つことを願っています。このように説明しましょう。マルチスレッドプログラムがほとんどのスレッドのいくつかのタスク(つまり、実行中のコード)のいくつかのタスクをカプセル化する必要がある場合、Threadlocalを使用して、カプセル化本体のスレッド関連のメンバー変数をラップして、スレッドアクセスの排他性を確保し、すべてのスレッドがカプセル化オブジェクトを共有できます。 Androidのルーパーを参照できます。コードの問題を説明できないプログラマーは、優れたプログラマーではありません。
コードを見てください:スレッドの時間のかかるコードを表示するためのツール(問題を説明するために作成)
statisticcosttime.java
//統計のコスト時のクラスstatisticCosttime {// starttimeを記録する// private threadlocal startlocal = new threadlocal(); private long starttime; // privatelocal costtime = new Sthreadlocal(); private long costtime; private statisticcosttime(){} {} // singletonpublic static statispublic statiscostime shareStance; {){){) static class class InstanceFactory {private static final StatisticCosttime instance = new StatisticCosttime();} // startpublic void start(){// starttime.set(system。nanotime()); startime = system.nanotime();} // endpublic boid end(){nanotime() System.nanotime() - starttime;} public long getstarttime(){return starttime; // return starttime.get();} public long getCosttime(){// return costtime.get(); return costtime;}}さて、ツール設計が完了しました。これを使用して、時間のかかるスレッドをカウントして試してみてください。
main.java
public class main {public static void main(string [] args)throws exception {// define define define define athread a = new shood(){// try {// record record timestatisticcosttime.shareinstance()。start(); speek(200); //開始時刻を印刷System.out.println( "a-starttime:"+statisticcosttime.shareinstance()。getStartTime(); // RecordStatisticCostTime.shareInstance() e){}}}; // start aa.start(); // define thread bthread b = new shood(){public void run(){// b1statisticcosttime.shareinstanceの開始時間を記録します。 consolesystem.out.println( "b1-starttime:"+statisticcosttime.shareinstance()。getstarttime()); b1system.out.println( "b1:"+statisticcosttime.shareinstance()。getCosttime()); // b2statisticcosttime.shareinstance() b2system.out.println( "b2-starttime:"+statisticcosttime.shareinstance()。getstarttime()); b2system.out.println( "b2:"+statisticcosttime.shareinstance()。getCostTime());} catch(Exception e){}}}}}; b.Start();}}}}コードを実行した後、出力結果は次のとおりです。出力結果の精度はナノ秒です。
結果が予想とは異なるかどうかに依存します。 Aの結果はB1+B2にほぼ等しい必要があることがわかりました。どうしてB2と同じになるのですか?答えは、開始時刻とコストタイムの変数を定義する場合、元の意図は共有されるべきではなく、スレッドに限定されるべきであるということです。ここでは、変数はSingletonと共有されるため、Aの値を計算するときに、開始時刻は実際にB2によって変更されているため、B2と同じ結果が出力されます。
次に、statisticCosttimeでコメントされた部分を開き、Threadlocalの宣言方法に変更して試してみましょう。
結果を参照してください:
ああ!これにより、予想される効果が達成されました。現時点では、一部の学生は、これがスレッド電流アクセスではないと言うでしょう。スレッドローカルを使用している限り、スレッドの安全性を確保できますか?答えはノーです!まず第一に、スレッドの安全性の問題がある理由を理解できますが、2つの状況しかありません。
1.スレッド間で共有されるべきではないリソースを共有しています。
2。スレッド間で共有されるリソースへの秩序あるアクセスを保証しません。
前者は、threadlocalを使用して「空間交換時間」メソッド(スレッドローカル変数を直接宣言することもできます)で解決でき、後者は「空間交換時間」メソッドで解決できます。これは明らかにthreadlocalができることではありません。
3。糸局所原理
実装の原則は実際には非常に単純です。 Threadlocalオブジェクトへの読み取りおよび書き込み操作は、実際にはスレッドの値オブジェクトの読み取りおよび書き込み操作です。ここでは、変数によって割り当てられたメモリ空間はTオブジェクトの保存に使用されないが、スレッドの値はTオブジェクトの保存に使用されるため、変数のコピーの作成はないことを明確にします。スレッド内のthreadlocalセットメソッドを呼び出すたびに、実際にはスレッドの対応する値オブジェクトにオブジェクトを書き込むプロセスです。 Threadlocal Getメソッドを呼び出すとき、それは実際には、スレッドの対応する値オブジェクトからオブジェクトを取得するプロセスです。
ソースコードを参照してください:
threadlocalメンバー変数セット
/***現在のスレッドのこの変数の値を設定します。 * {@code null}に設定されている場合、値はnullに設定され、基礎となるエントリは引き続き存在します。 * * @param値発信者スレッドの変数の新しい値。 */public void set(t value){thread currentthread = shood.currentthread();値値=値(currentThread); if(values == null){values = initializevalues(currentThread); } values.put(this、value);}TreadlocalメンバーメソッドGET
/***現在のスレッドのこの変数の値を返します。このスレッド上のこの変数に対してエントリ *がまだ存在しない場合、このメソッドは * @link #initialValue()}の結果とともに値を入力し、エントリを作成します。 * * @return呼び出しスレッドの変数の現在の値。 */@suppresswarnings( "unchecked")public t get(){//高速パスに最適化されています。スレッドcurrentThread = thread.CurrentThread();値値=値(currentThread); if(values!= null){object [] table = values.table; int index = hash&values.mask; if(this.reference == table [index]){return(t)table [index + 1]; }} else {values = initializevalues(currentThread); } return(t)values.getaftermiss(this);}threadlocalメンバーメソッド初期化
/***このスレッドと可変タイプの値インスタンスを作成します。 */values initializevalues(thread current){return current.localvalues = new values();}threadlocalメンバーメソッド値
/***このスレッドと可変タイプの値インスタンスを取得します。 */値値(スレッド電流){current.localvalues;}を返しますでは、この値でオブジェクトをどのように読み書きしますか?
値は、スレッドローカルの内部クラスとして存在します。この値には、重要な配列オブジェクト[]が含まれます。これは、質問に答える重要な部分です。スレッドにさまざまなタイプのトレッドロカル変数を保存するために使用されます。問題は、特定のタイプの変数を取得するときに他のタイプの値を取得しないようにするにはどうすればよいですか?一般に、マップはキー価値に従ってマッピングされます。はい、アイデアはこのアイデアですが、ここではMAPで実装されていません。オブジェクト[]で実装されたマップメカニズムです。ただし、MAPを使用してそれを理解する場合は、メカニズムが同じであるため不可能です。キーは実際にはthreadlocalの弱い参照に対応し、値は渡したオブジェクトに対応します。
オブジェクト[]を使用してマップメカニズムを実装する方法を説明しましょう(図1を参照)。アレイサブスクリプトのパリティを使用してキーと値を区別します。つまり、下の表にキーが均一な位置に保存され、奇数は値を保存します。これがどのように行われるかです。興味のある学生がアルゴリズムの実装を知りたい場合は、詳細に研究できます。ここでは詳しく説明しません。
前の最初の例に基づいて、ストレージの状況が分析されます。
プログラムが実行されると、3つのスレッドA、B、メインがあります。 name.set()がスレッドで呼び出されると、3つのスレッドインスタンスの3つの同一のメモリスペースが同時にヒープ領域に割り当てられ、値オブジェクトをキーとして参照を使用して値を保存し、特定のオブジェクトは3つの異なるオブジェクトに値として保存されます[]:以下の図を参照):
4。概要
Threadlocalは、マルチスレッドプログラミング中に並行性の問題を完全に解決することはできません。また、この問題には、さまざまな状況、「時間のスペース」または「スペースの時間」に従って選択するさまざまなソリューションが必要です。
Threadlocalの最大の機能は、スレッド共有変数をスレッドローカル変数に変換して、スレッド間の分離を実現することです。
上記は、JavaのThreadlocalをすばやく理解することです。私はそれが誰にでも役立つことを願っています。欠点がある場合は、それを指摘するためにメッセージを残してください。このサイトへのご支援をありがとうございました。