一般的な開発では、著者は、多くの学生がJava Concurrent Development Modelの扱いにいくつかの基本的な方法のみを使用しているとしばしば見ています。たとえば、揮発性、同期。ロックやアトミックなどの高度な同時パッケージは、多くの人にはあまり使用されません。理由のほとんどは、原則への属性の欠如によるものだと思います。忙しい開発作業では、誰が正しい並行性モデルを正確に把握して使用できますか?
そのため、最近、このアイデアに基づいて、私は並行性制御メカニズムを記事に整理する予定です。それはあなた自身の知識の記憶であるだけでなく、この記事で言及されているコンテンツがほとんどの開発者に役立つことを望んでいます。
並列プログラム開発には、必然的にマルチスレッドやマルチタスクのコラボレーションやデータ共有などの問題が含まれます。 JDKでは、複数のスレッド間で同時制御を実装するために複数の方法が提供されています。たとえば、一般的に使用される内部ロック、再突入ロック、読み取りワイトロック、セマフォ。
Javaメモリモデル
Javaでは、各スレッドにはワーキングメモリ領域があり、すべてのスレッドで共有されるメインメモリに変数の値のコピーを保存します。スレッドが実行されると、これらの変数が独自の作業メモリで動作します。
共有変数にアクセスするために、通常、スレッドはロックを取得し、ワーキングメモリ領域をクリアします。これにより、共有変数がすべてのスレッドの共有メモリ領域からスレッドのワーキングメモリ領域に正しくロードされます。スレッドのロックが解除されると、ワーキングメモリ領域の変数の値が共有メモリに関連付けられることが保証されます。
プログラムがスレッド同期操作を正しく使用するかどうかに関係なく、スレッドが特定の変数を使用する場合、取得する値は、それ自体または他のスレッドによって変数に保存される値でなければなりません。たとえば、2つのスレッドが異なる値またはオブジェクト参照を同じ共有変数に保存する場合、変数の値はこのスレッドまたはそのスレッドからのものであり、共有変数の値は2つのスレッドの参照値で構成されません。
変数が使用されているときにJavaプログラムがアクセスできるアドレス。基本的なタイプ変数と参照型変数だけでなく、配列タイプ変数も含まれます。メインメモリ領域に保存されている変数はすべてのスレッドで共有できますが、あるスレッドが別のスレッドのパラメーターまたはローカル変数にアクセスすることは不可能であるため、開発者はローカル変数のスレッド安全性の問題を心配する必要はありません。
揮発性変数は、複数のスレッド間で見ることができます
各スレッドには独自の作業メモリ領域があるため、1つのスレッドが独自の作業メモリデータを変更すると、他のスレッドには見えない場合があります。これを行うには、揮発性キーワードを使用してすべてのスレッドを破壊してメモリ内の変数を読み書きして、揮発性変数が複数のスレッド間で表示されるようにすることができます。
揮発性として宣言された変数は、次のように保証できます。
1.他のスレッドによる変数の変更は、現在のスレッドに迅速に反映できます。
2.現在のスレッドの揮発性変数の変更を、時間内に共有メモリに書き戻し、他のスレッドで見ていることを確認してください。
3.揮発性によって宣言された変数を使用すると、コンパイラは秩序を確保します。
同期されたキーワード
同期されたキーワード同期は、Java言語で最も一般的に使用される同期方法の1つです。初期のJDKバージョンでは、Synchronizedのパフォーマンスはあまり良くなく、ロック競争が特に激しくない場合には価値が適していました。 JDK6では、同期されたロックと不公平なロックの間のギャップが狭まりました。さらに重要なことに、同期はより簡潔で明確であり、コードは読みやすく維持されています。
オブジェクトをロックする方法:
public synchronized void method(){}
メソッド()メソッドが呼び出されると、呼び出しスレッドは最初に現在のオブジェクトを取得する必要があります。現在のオブジェクトロックが他のスレッドによって保持されている場合、呼び出しスレッドが待機します。違反が終了した後、オブジェクトロックがリリースされます。上記の方法は、次の記述方法と同等です。
public void method(){synchronized(this){//何かをする…}}第二に、同期化されたものを使用して、同期ブロックを構築することもできます。同期方法と比較して、同期ブロックは同期コードの範囲をより正確に制御できます。小さな同期コードは、ロックの内外で非常に高速であるため、システムがより高いスループットを提供します。
public void method(object o){// beforesynchronized(o){//何かをします...} //後}同期は、静的関数にも使用できます。
public同期static void method(){}
この場所では、同期されたロックが現在のクラスオブジェクトに追加されることに注意することが重要です。そのため、このメソッドへのすべての呼び出しはクラスオブジェクトのロックを取得する必要があります。
同期は、オブジェクトまたはコードセグメントのスレッドの安全性を確保することができますが、同期単独でのみ使用するだけでは、複雑なロジックとのスレッド相互作用を制御するにはまだ十分ではありません。複数のスレッド間の相互作用を実現するには、オブジェクトオブジェクトのwait()とnotify()メソッドも必要です。
典型的な使用法:
同期(obj){while(<?>){obj.wait(); //通知を受け取った後も実行を続けます。 }} wait()メソッドを使用する前に、オブジェクトロックを取得する必要があります。 wait()メソッドが実行されると、現在のスレッドは、他のスレッドが使用するためにOBJの排他的ロックをリリースする場合があります。
OBJのスレッドがOBJ.Notify()を受信するのを待つとき、OBJの排他的ロックを取り戻して実行を続けることができます。 Notify()メソッドは、現在のオブジェクトを待っているスレッドをランダムに呼び起こすことであることに注意してください。
これは、ブロッキングキューの実装です。
public class blockqueue {private list list = new arrayList(); public synchronized object pop()throws arturnedexception {while(list.size()== 0){this.wait(); } if(list.size()> 0){return list.remove(0); } else {return null; }} public synchronized object put(object obj){list.add(obj); this.notify(); }}同期してwait()およびnotify()は、Java開発者が習得しなければならない基本的なスキルである必要があります。
REENTRANTLOCK REENTRANTLOCK LOCK
ReentrantLockはReentrantLockと呼ばれます。同期するよりも強力な機能を備えており、中断して時間がかかります。同時性が高い場合、同期したパフォーマンスの利点が明らかになります。
ReentrantLockは、公正なロックと不公平なロックの両方を提供します。公正なロックはロックの最初のファーストアウトであり、公正なロックを並べることはできません。もちろん、パフォーマンスの観点から見ると、不公平なロックのパフォーマンスははるかに優れています。したがって、特別なニーズがない場合、不公平なロックを好むはずですが、同期されたロック業界は絶対に公平ではありません。 ReentrantLockは、構築時にロックが公正かどうかを指定できます。
再突入ロックを使用するときは、プログラムの最後にロックをリリースしてください。一般に、ロックをリリースするためのコードは最終的に記述する必要があります。それ以外の場合、プログラムの例外が発生した場合、ロックは決してリリースされません。同期ロックは、最後にJVMによって自動的に解放されます。
古典的な使用法は次のとおりです。
try {if(lock.trylock(5、timeUnit.seconds)){//ロックされている場合は、5秒を待ってロックを取得できるかどうかを確認してください。 5秒後にロックを取得できない場合は、falseを返して実行を続行します割り込みイベントに応答することができます{//操作}最後に{lock.unlock(); }}} catch(arturnedexception e){e.printstacktrace(); //現在のスレッドが中断されると(割り込み)、中断されたエクセプトがスローされます}ReentrantLockは、豊富な種類のロック制御機能を提供し、アプリケーションのパフォーマンスを改善するためにこれらの制御方法を柔軟に適用します。ただし、ここでReentrantLockを使用することはあまりお勧めしません。再入国ロックは、JDKで提供される高度な開発ツールです。
readwriteLock読み取りおよび書き込みロック
分離の読み取りと書き込みは、非常に一般的なデータ処理のアイデアです。 SQLで必要な技術と見なす必要があります。 readwriteLockは、JDK5で提供される読み取りワイト分離ロックです。分離ロックの読み取りと書き込みは、システムのパフォーマンスを向上させるために、ロック競争を削減するのに効果的に役立ちます。読み取りと書き込みを分離するための使用シナリオは、主にシステム内で、読み取り操作の数が書き込み操作よりもはるかに大きい場合です。それを使用する方法は次のとおりです。
Private ReentrantreadWritelock readwritelock = new Reentrantreadwritelock(); private lock readlock = readwritelock.readlock(); private lock writelock = readwriteLock.writeLock(); public object handleread()throws interruptedexception {try {readlock.lock.lock(); thread.sleep(1000);返品値。 }最後に{readlock.unlock(); }} public object handleread()throws arturnedexception {try {writelock.lock(); thread.sleep(1000);返品値。 }最後に{writelock.unlock(); }}条件オブジェクト
ConditionDオブジェクトは、複数のスレッド間の複雑なコラボレーションを調整するために使用されます。主にロックに関連付けられています。ロックにバインドされた条件インスタンスは、ロックインターフェイスのnewCondition()メソッドを介して生成できます。条件オブジェクトとロックの関係は、2つの関数object.wait()、object.notify()、および同期キーワードを使用するようなものです。
ここで、配列のソースコードを抽出できます。
パブリッククラスの配列blockingQueue拡張アブストラクトキューを実装するblockingqueue、java.io.serializable {/**すべてのアクセスのメインロックガード*/最終的なRenentrantLockロック;/**条件待機の条件*/**条件待機中の条件*/プライベート最終条件著作IllegalArgumentException(); this.items = new object [caperation]; lock = new ReentrantLock(FAIR); notempty = lock.newcondition(); //条件を生成notfull = lock.newcondition();} public void put(e e)throws interruptedexception {checknotnull(e); final reentrantlock lock = this.lock; lock.lockinterrumdibly(); try {while(count == items.length)notfull.await();挿入(e); }最後に{lock.unlock(); }} private void insert(e x){items [putindex] = x; putindex = inc(putindex); ++カウント; notempty.signal(); //通知} public e take()throws arturnedexception {final reentrantlock lock = this.lock; lock.lockinterrumdibly(); try {while(count == 0)//キューが空の場合はnotempty.await(); //消費者のキューは、空でない信号返品抽出()を待つ必要があります。 }最後に{lock.unlock(); }} private e Extract(){final object [] items = this.items; e x = this。<e> cast(items [takeindex]); items [takeindex] = null; takeIndex = inc(takeindex); - カウント; notfull.signal(); // put()は、スレッドキューには空きスペースが戻っているx;} //他のコード}があることを通知します} Semaphore Semaphore <BR /> Semaphoreは、マルチスレッドコラボレーションのためのより強力な制御方法を提供します。セマフォはロックの拡張機能です。内部ロック同期であろうとReentrantLockであろうと、1つのスレッドで一度にリソースにアクセスできますが、セマフォは複数のスレッドが同時にリソースにアクセスすることを指定できます。コンストラクターから、私たちは見ることができます:
パブリックセマフォ(int許可){}
パブリックセマフォ(int許可、ブールフェア){} //それが公正かどうかを指定できます
許可証は、セマフォのアクセスブックを指定します。つまり、同時にいくつのライセンスを適用できるかを意味します。各スレッドが一度に1つのライセンスにのみ適用される場合、これは特定のリソースに同時にアクセスできるスレッドの数を指定することに相当します。使用する主な方法は次のとおりです。
public void acture()throws arturtedexception {} //アクセス許可を取得しようとします。使用できない場合、スレッドが許可を解放するか、現在のスレッドが中断されていることを知って、スレッドが待機します。
public void acchireuninterrumdivily(){} // acchire()に似ていますが、割り込みには応答しません。
public boolean tryacquire(){} //それを取得してみてください。この方法は待たず、すぐに戻ります。
public boolean tryacquire(長いタイムアウト、TimeUnitユニット)スローが中断されます{} //待つのにどれくらい時間がかかりますか
public void release()//は、許可を待っている他のスレッドがリソースにアクセスできるように、オンサイトアクセスリソースが完了した後にライセンスをリリースするために使用されます。
JDKドキュメントで提供されているセマフォを使用する例を見てみましょう。この例では、セマフォを介してリソースアクセスを制御する方法を説明しています。
パブリッククラスプール{private static final int max_available = 100; private final semaphore vayaver = new semaphore(max_available、true); public object getItem()throws interruptedexception {abainal.acquire(); //ライセンスを申請する// 100個のスレッドのみが入力して同時に利用可能なアイテムを取得できます。 //利用可能なアイテムを追加し、ライセンスをリリースすると、リソースを要求するスレッドがアクティブになります}} //たとえば、リファレンスデータ保護オブジェクト[] items = newオブジェクト[max_available]; //オブジェクトプールマルチプレックスオブジェクトに使用されるブールアレン[]使用= new boolean [max_available]; //マークアップ機能保護された同期オブジェクトreturnアイテム[i]; }} return null;}保護された同期ブールマルカスナューズ(オブジェクトアイテム){for(int i = 0; i <max_available; ++ i){if(item == items [i]){if(used [i]){used [i] = false; trueを返します。 } else {return false; }}} false;}}このインスタンスは、最大容量が100のオブジェクトプールを実装するだけです。したがって、100個のオブジェクト要求が同時にある場合、オブジェクトプールにリソース不足があり、リソースを取得できないスレッドが待機する必要があります。オブジェクトを使用してスレッドが終了したら、オブジェクトをオブジェクトプールに返す必要があります。この時点で、利用可能なリソースが増加するため、リソースを待機するスレッドをアクティブにすることができます。
threadlocal threadローカル変数<br /> threadlocalに連絡し始めた後、このスレッドローカル変数の使用シナリオを理解することは困難です。今振り返ってみると、ThreadLocalは複数のスレッド間の変数への同時アクセスのためのソリューションです。同期やその他のロック方法とは異なり、Threadlocalはロックをまったく提供しませんが、各スレッドに変数の独立したコピーを提供するために時間にスペースを交換する方法を使用して、スレッドの安全性を確保します。したがって、それはデータ共有の解決策ではありません。
Threadlocalは、スレッドの安全性の問題を解決するための良い考えです。 Threadlocalクラスには、各スレッドの変数のコピーを保存するマップがあります。マップ内の要素のキーはスレッドオブジェクトであり、値はスレッドの変数のコピーに対応します。キー値を繰り返すことができないため、各「スレッドオブジェクト」はスレッドの「変数のコピー」に対応し、スレッドの安全に達します。
特に注目に値します。パフォーマンスに関しては、Threadlocalには絶対的なパフォーマンスがありません。並行性のボリュームがそれほど高くない場合、ロックのパフォーマンスが向上します。ただし、ロックとはまったく関係のないスレッドセーフソリューションのセットとして、Threadlocalを使用すると、高い並行性または激しい競争でロック競争をある程度減らすことができます。
こちらがthreadlocalの簡単な使用です:
public class testnum {// threadlocalのinitialValue()メソッドを匿名の内側クラスを介して上書きするには、初期値を指定します。 }}; //次のシーケンス値を取得しますpublic int getNextnum(){seqnum.set(seqnum.get() + 1); return seqnum.get();} public static void main(string [] args){testnum sn = new testnum(); // 3スレッドはSNを共有し、それぞれがシーケンス番号TestClient T1 = new testClient(SN)を生成します。 testClient T2 = new testClient(SN); testClient T3 = new testClient(SN); t1.start(); t2.start(); t3.Start(); } private static class testclientはスレッドを拡張します{private testnum sn; public testclient(testnum sn){this.sn = sn; } public void run(){for(int i = 0; i <3; i ++){//各スレッドは3つのシーケンス値system.out.println( " + thread.currentthread()。 }}}}出力結果:
スレッド[スレッド-0]> sn [1]
スレッド[スレッド1]> sn [1]
スレッド[スレッド2]> sn [1]
スレッド[スレッド1]> sn [2]
スレッド[スレッド-0]> sn [2]
スレッド[スレッド1]> sn [3]
スレッド[スレッド2]> sn [2]
スレッド[スレッド-0]> sn [3]
スレッド[スレッド2]> sn [3]
出力結果情報は、各スレッドによって生成されたシーケンス番号が同じtestnumインスタンスを共有しているが、互いに干渉しないが、それぞれが独立したシーケンス番号を生成することを発見することができます。これは、threadlocalが各スレッドに個別のコピーを提供するためです。
「ロック」のロックパフォーマンスと最適化は、最も一般的に使用される同期方法の1つです。通常の開発では、多くの学生がロックに大きなコードを直接追加することがよくあります。一部の生徒は、すべての共有問題を解決するために1つのロック方法のみを使用できます。明らかに、そのようなエンコードは受け入れられません。特に高い並行性環境では、激しいロック競争は、プログラムのより明白なパフォーマンスの低下につながります。したがって、ロックの合理的な使用は、プログラムのパフォーマンスに直接関連しています。
1。スレッドオーバーヘッド<br />マルチコアの場合、マルチスレッドを使用すると、システムのパフォーマンスが大幅に向上する可能性があります。ただし、実際の状況では、マルチスレッドを使用すると、システムオーバーヘッドが追加されます。シングルコアシステムタスク自体のリソース消費に加えて、マルチスレッドアプリケーションは、追加のマルチスレッドユニークな情報を維持する必要もあります。たとえば、スレッド自体のメタデータ、スレッドスケジューリング、スレッドコンテキストの切り替えなど。
2。ロック保持時間を減らします
ロックを同時制御に使用するプログラムでは、ロックが競合するとき、単一のスレッドのロック保持時間はシステムパフォーマンスと直接的な関係を持ちます。スレッドが長い間ロックを保持している場合、ロックの競争はより激しくなります。したがって、プログラム開発の過程で、スレッド間の相互除外の可能性を減らすために、特定のロックを占有する時間を最小限に抑える必要があります。たとえば、次のコード:
public synchronized void syncmehod(){beforemethod(); mutexmethod(); aftermethod();}このインスタンスのmutexmethod()メソッドのみが同期しているが、beforemethod()およびaftermethod()では、同期制御を必要としない場合。 beforemethod()およびaftermethod()がヘビー級の方法である場合、CPUには長い時間がかかります。この時点で、同時性が大きい場合、この同期スキームを使用すると、待機スレッドが大幅に増加します。現在実行中のスレッドは、すべてのタスクが実行された後にのみロックをリリースするためです。
以下は最適化されたソリューションであり、必要に応じて同期するため、スレッドがロックを保持する時間を大幅に減らし、システムのスループットを改善できるようにします。コードは次のとおりです。
public void syncmehod(){beforemethod(); synchronized(this){mutexmethod();} aftermethod();} 3.ロック粒子サイズを縮小します
ロックの粒度を減らすことは、マルチスレッドロックの競争を弱めるための効果的な手段でもあります。このテクノロジーの典型的な使用シナリオは、同時ハッシュマップクラスです。通常のハッシュマップでは、コレクションでadd()操作またはget()操作が実行されるたびに、コレクションオブジェクトのロックが常に取得されます。ロックはコレクションオブジェクト全体にあるため、この操作は完全に同期動作です。したがって、高い並行性では、激しいロック競争はシステムのスループットに影響します。
ソースコードを読んだ場合、Hashmapが配列 +リンクリストに実装されていることを知っておく必要があります。 CONCURRENTHASHMAPは、ハッシュマップ全体をいくつかのセグメント(セグメント)に分割し、各セグメントはサブハッシュマップです。新しいテーブルエントリを追加する必要がある場合は、ハッシュマップをロックしません。 20回の検索行は、ハッシュコードに従ってテーブルエントリを保存するセクションを取得し、セクションをロックしてput()操作を完了します。このようにして、マルチスレッド環境では、複数のスレッドが同時に書き込み操作を実行する場合、記述されているアイテムが同じセグメントに存在しない限り、スレッド間で真の並列性を達成できます。特定の実装のために、読者がConcurrenthashmapクラスのソースコードを読むのに時間がかかることを願っています。そのため、ここではあまり説明しません。
4.ロック分離<BR /> a readwritelock前述のreadwritelock読み取りおよび書き込みロック、次に、読み取りと書き込みの分離の拡張はロックの分離です。ロック分離のソースコードは、JDKにもあります。
パブリッククラスのリンク済みブロッキングキュー拡張アブストラクトキューを実装するblockingqueue、java.io.serializable {/*lock hold by take、popllなど、プライベート最終Reentrantlock takelock = new reintrantlock();/**待機の待機キューputlock = new ReentrantLock();/**待機するための待機キューint c = -1; final atomicinteger count = this.count; final reentrantlock takelock = this.takelock; takelock.LockIntructibly(); //同時にデータを読むための2つのスレッドがありません{while(count.get()== 0){//利用可能なデータがない場合は、put()notempty.await()の通知を待ちます。 } x = dequeue(); //アイテムを削除c = count.getanddecrement(); //サイズマイナス1 if(c> 1)notempty.signal(); // other other take()Operations}を通知}最後に{takelock.unlock(); //ロックをリリース} if(c ==容量)signalnotfull(); // notify put()操作、既に空きスペースリターンx;} public void put(e e)throws arturnedexception {if(e == null)throw new nullpointerexception(); //注:すべてのPut/Take/etcの慣習は、地元のvarをプリセットすることです。 int c = -1; node <e> node = new node(e); final reentrantlock putlock = this.putlock; final atomicinteger count = this.count; putlock.LockIntructibly(); // 2つのスレッドが同時にデータを置くことはできません{ / * *ロックで保護されていない場合でも、カウントは待機ガードで使用されることに注意してください。これは、カウントがこの時点でのみ減少する可能性があるため(他のすべてのプットはロックによって閉まります)、容量から変更された場合は *署名されます。同様に *他の待機警備員の他のすべてのカウントの使用について。 */ while(count.get()==容量){//キューがいっぱいの場合は、wait notfull.await(); } enqueue(node); //キューc = count.getandincrement(); // size plus 1 if(c + 1 <容量)notfull.signal(); //他のスレッドに十分なスペースがある場合}最後に{putlock.unlock(); // lock} if(c == 0)signalnotempty(); //挿入が成功した後、データを読み取るために操作を通知} //他のコード}ここで説明する必要があるのは、take()とput()関数は互いに独立しており、それらの間にロック競争関係がないということです。 TakelockとPutlockをそれぞれのTake()とput()の中で競争する必要があります。したがって、ロック競争の可能性は弱まります。
5.ロックの粗さ<BR />上記のロック時間と粒度の縮小は、各スレッドがロックを保持する最短時間を満たすために行われます。ただし、粒度で程度を把握する必要があります。ロックが絶えず要求され、同期し、リリースされている場合、システムの貴重なリソースを消費し、システムオーバーヘッドを増やします。
私たちが知っておくべきことは、仮想マシンが同じロックの一連の継続的な要求とリリースに遭遇すると、すべてのロック操作を1つの要求にロックに統合し、それによりロックのリクエストの数を減らすことです。この操作は、ロックの粗さと呼ばれます。統合例のデモは次のとおりです。
public void syncmehod(){synchronized(lock){method1();} synchronized(lock){method2();}} jvm統合後の形式:public void syncmehod(){synchronized(lock){method1(); method2();}}}}}したがって、このような統合により、開発者はロック粒度の把握に良いデモンストレーション効果を与えます。
ロックレスパラレルコンピューティング<br />上記は、ロックについて話すことに多くの時間を費やしました。また、ロックは特定のコンテキストの切り替えに追加のリソースオーバーヘッドをもたらすことも言及されています。同時性が高いため、「ロック」のための激しい競争はシステムのボトルネックになる可能性があります。したがって、ここでは非ブロッキング同期方法を使用できます。このロックフリーメソッドは、データとプログラムが高い並行性環境で複数のスレッド間の一貫性を維持することを保証できます。
1。非ブロッキング同期/ロックレス
非ブロッキング同期法は、実際には以前のThreadlocalに反映されています。各スレッドには変数の独自の独立したコピーがあるため、並行して計算するときにお互いを待つ必要はありません。ここでは、著者は主に、CASCASアルゴリズムの比較とスワップに基づいて、より重要なロックフリーの並行制御方法を推奨しています。
CASアルゴリズムのプロセス:3つのパラメーターCAS(V、E、N)が含まれます。 vは更新する変数を表し、eは期待値を表し、nは新しい値を表します。 Vの値は、V値がE値に等しい場合にのみnに設定されます。 V値がe値と異なる場合、他のスレッドが更新を行っており、現在のスレッドは何もしません。最後に、CASは現在のVの真の値を返します。CASを操作するとき、それは楽観的な態度で実行され、操作を正常に完了できると常に考えています。複数のスレッドがCASを使用して変数を同時に操作する場合、ジュンフイの残りの部分が失敗しますが、1つだけが勝ち、正常に更新されます。故障したスレッドは中断されず、障害が許可されていることのみが通知され、再試行することが許可されており、もちろん失敗したスレッドも操作を放棄することを可能にします。この原則に基づいて、CASの動作はロックなしでタイムリーであり、他のスレッドは現在のスレッドへの干渉を検出し、適切に処理することもできます。
2。原子量操作
JDKのjava.util.concurrent.atomicパッケージは、ロックフリーアルゴリズムを使用して実装された原子動作クラスを提供し、コードは主に基礎となるネイティブコード実装を使用します。興味のある学生は、ネイティブレベルのコードを引き続き追跡できます。ここには表面コードの実装は投稿しません。
以下は、主に例を使用して、通常の同期方法とロックフリー同期の間のパフォーマンスギャップを示します。
パブリッククラスのテスタトミック{private static final int max_threads = 3; private static final int task_count = 3; private static final int target_count = 100 * 10000; private atomicintegerアカウント= new AtomicInteger(0); private int count = 0;同期int inc(){return ++ count count; {文字列名;長い早い時期;テスタニックアウト; public syncthread(TestAtomic O、Long Starttime){this.out = o; this.starttime = starttime; } @Override public void run(){int v = out.inc(); while(v <target_count){v = out.inc(); } long endtime = system.currenttimemillis(); system.out.println( "syncthread spend:" +(endtime -starttime) + "ms" + "、v =" + v); }} public class atomicthread runnable {string name;長い早い時期; Public AtomicThread(long starttime){this.starttime = starttime; } @Override public void run(){int v = account.incrementAndget(); while(v <target_count){v = account.incrementAndget(); } long endtime = system.currenttimemillis(); System.out.println( "AtomicThread spend:" +(endtime -starttime) + "ms" + "、v =" + v); }}@testpublic void testsync()throws arturnedexception {executorservice exe = executors.newfixedthreadpool(max_threads); long starttime = system.currenttimemillis(); syncthread sync = new syncthread(this、starttime); for(int i = 0; i <task_count; i ++){exe.submit(sync); } thread.sleep(10000);}@testpublic void testatomic()throws arturnedexception {executorservice exe = executors.newfixedthreadpool(max_threads); long starttime = system.currenttimemillis(); AtomicThread Atomic = new AtomicThread(starttime); for(int i = 0; i <task_count; i ++){exe.submit(atomic); } thread.sleep(10000);}}テスト結果は次のとおりです。
testsync():
SyncThread支出:201ms、v = 1000002
Syncthread支出:201ms、v = 1000000
SyncThread支出:201ms、v = 1000001
testatomic():
AtomicThread支出:43ms、v = 1000000
AtomicThread支出:44ms、V = 1000001
AtomicThread支出:46ms、V = 1000002
このようなテスト結果は、内部ロックと非ブロッキング同期アルゴリズムのパフォーマンスの違いを明確に反映していると思います。したがって、著者は、この原子クラスをAtomicの下で直接考慮することを推奨しています。
結論
最後に、私は表現したいことを整理しました。実際、言及されていないCountDownLatchのようなクラスがまだいくつかあります。ただし、上記のことは間違いなく同時プログラミングの中核です。おそらく、一部の読者はインターネット上でそのような多くの知識ポイントを見ることができるかもしれませんが、私はまだ、比較して、適切な使用シナリオで知識を見つけることができると考えています。したがって、これが編集者がこの記事をまとめた理由でもあり、この記事がより多くの学生に役立つことを願っています。
上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。