著者:acupt
序文
マルチスレッドの同時コンテナクラスを考慮せずに、一般的に、スレッドセーフなクラス、より効率的なとしてのArrayListを、HashMapの使用。同時実行のシナリオでは、多くの場合、いくつかの効率を犠牲にして、このようなスレッドセーフな、けれどもとしてのConcurrentHashMap、ArrayBlockingQueueコンテナを使用しますが、それは安全でした。
上記のスレッドセーフなコンテナはjava.util.concurrentパッケージであり、このパッケージの同時コンテナの多くは、今日のすべては、それをいじくり回すことが判明しました。
短時間だけ行い、その後、フォローアップ、それぞれの深い探査。
コンカレント・コンテナーの紹介
ConcurrentHashMapの:並行バージョンのHashMap
CopyOnWriteArrayListと:ArrayListの同時のバージョン
CopyOnWriteArraySet:同時設定
ConcurrentLinkedQueue:同時キュー(リストに基づいて)
ConcurrentLinkedDeque:(双方向リンクリストに基づいて)同時キュー
ConcurrentSkipListMapの:同時マップベースジャンプテーブル
ConcurrentSkipListSetの:セットベースの同時ジャンプテーブル
ArrayBlockingQueue:キューをブロック(アレイベース)
LinkedBlockingQueue:(リストに基づく)のブロッキングキュー
LinkedBlockingDeque:キューを遮断する(双方向リンクリストに基づいて)
PriorityBlockingQueue:セキュリティスレッドのプライオリティキュー
SynchronousQueue:キュー・ペアを読みます
LinkedTransferQueue:データ交換キューリストに基づいて、
DelayQueue:遅延キューイング
1.ConcurrentHashMap同時バージョンのHashMap
最も一般的なコンカレント・コンテナーの一つは、同時シナリオの下でキャッシュとして使用することができます。底はまだハッシュテーブルであるが、Java 8でない小さな変化に、そしてJAVA 7 JAVA 8とその他のリリースを使用しており、両方のバージョンは、多くの場合のようないくつかの比較の実装を(行いますインタビュー)。
比較的大きな違いは、競争のロックを減らすためにサブロックとJAVA 7であるCAS(楽観的ロック)を使用して放棄ロックセグメントにおけるJAVA 8、およびハッシュリストに重大な競合縮退を防止するために、(変換はこの位置で衝突のリストを生成した後、オブジェクトは共に同じハッシュ値チェーン)は、鎖長の赤黒木に閾値(8)に達した(リストと比較して、ツリークエリ効率)がより安定です。
2.CopyOnWriteArrayList同時バージョンのArrayList
ArrayListの、下にある構造の同時バージョンは配列で、その差は、ArrayListのは、ということである:時間を追加および削除する際の要素の新しい配列を作成し、新しい配列内の特定のオブジェクトを追加したり、除外し、最終的にアレイの新しい配列とオリジナルを置き換えます。
したがって、あまり適したシーンを読み書きのための読み出し動作と、書き込みロックしない(追加、削除、変更)操作ロック、:該当シーン。
制限事項:時間は、(高効率を読み、ArrayListのような一般的な、)現在のコピーの読み取りをロックを読みませんので、それは汚れたデータを読み取ることができます。心の場合は、お勧めしません。
ソースの気持ちを見てください:
CopyOnWriteArrayListとクラス<E>公共 、道具一覧<E>ランダム・、Cloneableを、java.io.Serializableの{ 決勝ReentrantLockのロックのReentrantLockの(の=新しい新しい過渡); プライベート過渡揮発性のオブジェクト[]配列; //要素を追加し、ロック 公共ブールのアドオンを(E E){ ReentrantLockの= this.lockの最終ロック; Lock.lock(); //変性ロック、並行性、安全確保 の試行を{ オブジェクト[] =のgetArray要素(); //現在の配列 INT LEN = elements.length ; [] =たnewElements(要素、LEN用+ 1)Arrays.copyOfオブジェクト; //新しい配列、古いよりも大きい空間作成 されたnewElements [LEN] = Eを; //新しい配列に追加される要素 はsetArray(たnewElements ); //新しい配列で、元の配列を置き換える 真のリターン。 }最後に{ lock.unlock(); //ロック解除 } } 古いデータを読み出すことができるよう//読み取り要素は、ロックされていない パブリックEのGET(INTインデックス){ 戻りGET(のgetArray()、指数); }}
3.CopyOnWriteArraySet同時設定
CopyOnWriteArrayListと実現(CopyOnWriteArrayListとは、メンバ変数が含まれている)から、それは底が配列であると言うことです、それぞれが(ロック)を挿入する必要があるかどうかを知るために、コレクション全体を通過しなければならない存在しないを追加することを意味します。
該当するシーン:追加の適用CopyOnWriteArrayListとのシーンでは、あまりにも大規模なコレクション(すべての怪我は余裕がないトラバース)。
(リストに基づいて)4.ConcurrentLinkedQueue同時キュー
同時キューを達成するためのリストに基づいて、スレッドの安全性を確保するために楽観的ロック(CAS)を使用します。データ構造がリストであるので、理論的には何のキューサイズの制限がないので、それはデータが成功する追加することです。
(双方向リンクリストに基づいて5.ConcurrentLinkedDeque同時キュー)
FIFO(FIFO)に加えて、アウト(FILO)とすることができるので、同時双方向リンクリストキュー頭と尾を個別に操作することができ、それが達成コールスタックの後に前進する必要があることを、当然のことながら、後に進みました。
6.ConcurrentSkipListMapジャンプテーブルベースの同時実行の地図
SkipList即ちジャンプテーブルは、テーブルは、空間データ、時間構造、冗長データ、層によってリスト率層、同様の効果バイナリ検索を達成することでジャンプ
同時設定に基づいてテーブルをジャンプ7.ConcurrentSkipListSet
HashSetのとConcurrentSkipListMapのあるHashMapの、ConcurrentSkipListSetの関係と同様に、彼は精巧ではないでしょう。
8.ArrayBlockingQueueブロッキングキュー(アレイベース)
入力された際に構造が、開発しなければならない時に基づき、ブロッキングキューは、配列、配列のサイズで実現することができる配列が一杯になった場合のものを入れるロックReentrantLockの保証スレッドセーフで、位置は(も直接リターンをサポートし、タイムアウトを待つ)までブロックします。
提供動作と例えば:
クラス公開<E>はAbstractQueue <E>の延びとArrayBlockingQueue 器具のBlockingQueue <E>、java.io.Serializableの{ / ** *このロック共有の読み書きは、次の2つのスレッド間の通信状態で 密接*との両方の状態をロックします連絡先(つまり、ロックを生成する方法である) *通知/待機に類似オブジェクト * / ReentrantLockのの最終的なロックを; / **信号キューが空でなく、注意がスレッド* /フェッチする必要 条件条件notEmptyのための最終的なプライベートを; / **何のキューませんフル信号は、データのスレッドが* /集中する必要があり、書き込み 条件条件notFullのための民間最終; //ブロック取るために何かありますまで テイクを公共Eを()InterruptedExceptionある{スロー 決勝ReentrantLockのロック= this.lock; lock.lockInterruptiblyを(); 試してみます{ 一方(COUNT == 0) notEmpty.await(); 戻りデキュー(); } {最後に lock.unlockは(); } } //キューがいっぱいになったときに指定した時間を待って、最後に要素を挿入、または返します入れない場合は 、パブリックブールのオファーを(E電子ロングタイムアウト、TimeUnitでユニット) InterruptedExceptionある{スロー ; checkNotNull(E) またはnanos =(タイムアウト)のロングunit.toNanos、 最終的なロックがReentrantLockの= this.lockの; lock.lockInterruptibly(); //ロック {試して //サイクルまで待ちますアイドルキュー 一方(COUNT == items.length){ IF(nanos値の<= 0) 偽に戻り; //待機タイムアウト、リターン 一時的に)おそらく、早起きしてロックをつかむ、ループ条件を決定する必要がある(いくつかの時間を待って、ロックを解除する// //この時、他のスレッドがかかる場合があります離れて要素は、その挿入する機会があります = nanos値(nanos値の)のnotFull.awaitNanos; } エンキュー(E); //要素の挿入 、真への復帰を 、最終的に{} lock.unlock(); //ロック解除を } }
一見ただの時間の空気はここにスレッドを読むためにあれば、それをブロックしなかったであろうことを、少しは読んで、混乱と書き込みが同じロックされていますか?
ロックは、同様の同期化+の機能を通知+待っていたように、答えは、notFull年、notEmptyにあるロックからこれら二つのささいなこと。ポータル→最終的に睡眠を知ってもらう/通知/待つ/のnotifyAll
9.LinkedBlockingQueueのブロッキングキュー(リストに基づいて)
あなたが最大のintにデフォルトを設定しない場合は、キューをブロック達成するためのリストに基づいて、非ブロックConcurrentLinkedQueueの比率をしたいと思い、それは、容量制限を超えています。
(双方向リンクリストに基づいて)10.LinkedBlockingDequeのブロッキングキュー
同様にLinkedBlockingQueueが、ユニークな操作の二重リンクリストを提供します。
11.PriorityBlockingQueueプライオリティキュースレッドセーフ
コンパレータを構築するときに渡すことができ、袋の要素がソートされ、その後、順番を読んで時間を費やすことになると、見ることができます。優先度の高い要素があったので、長期的な消費ではないかもしれないいくつかの低優先度の要素が入ってきます。
12.SynchronousQueue握手データキュー
それは要素を格納するための本当のスペースがなかったため、偽のキューは、各挿入オペレーションは、対応する削除操作がないときは出し続けることができない持っている必要があります。
簡単な例を感じます。
* java.util.concurrentのインポート、{メインpublicクラス。 パブリック静的無効メイン(文字列[] args){ SynchronousQueue <整数> =新しい新しいキューSynchronousQueue <>(); 新しい新しいThread(() - > { 試み{ //ません残り、クレイジー書き込み (I = 0をint型;;私は++){のため のSystem.out.println( "追加:" + I); queue.put(I); } }キャッチ(InterruptedExceptionあるE){ e.printStackTrace( ); } })スタート(); 新しい新しいThread(() - > { 試み{ //塩漬けモードをフェッチ 中(真){ System.out.println( "とら:" + queue.take()); のThread.sleep((ロング)(Math.random()* 2000)); } }キャッチ(InterruptedExceptionあるE){ e.printStackTrace(); } 。})スタート(); }} / *出力: 追加:0 削除:0 へ:1 取ら:1 追加:2 とら:2 追加:3を 除去:3 *を/
スレッドが何らかの睡眠なしで書き込みを見ることができ、しばらくの間、別の睡眠の後に1を読んで、別のスレッドが非常に肯定読みながら、完全に、物事のキューに入れられると言うことができます。得られた出力は、読み出しおよび書き込み動作は、ペアに発生されます。
使用シナリオでJAVAはExecutors.newCachedThreadPool()、キャッシュスレッドプールを作成することです。
ExecutorServiceのnewCachedThreadPool静的パブリック(){ 新しいて、新しいThreadPoolExecutor(戻り 0、0 //コア糸を、無駄なスレッドは無慈悲である放棄 Integer.MAX_VALUEでは、スレッドの//最大数は、理論的には無制限ではなく、マシンリソースの値に空にした 60Lの後、TimeUnit.SECONDS、//破壊するために60秒間アイドル状態のスレッド 新しいSynchronousQueue <Runnableを>()) ; アイドルスレッドのタスクは失敗しますがあればアウト//提供時間は、それは、スレッドプールのスレッドを作成します。 }
データ交換キューに基づいて13.LinkedTransferQueueリスト
あなたは転写法により要素を配置する際に待機しているスレッドに、この要素に直接要素を取るにブロックされたスレッドがある場合は、インタフェースTransferQueueを実装しています。誰もが消費者を待っていない場合、誰かがこの要素を読み取るまで、それはキューの末尾、およびこの方法のブロックに、この要素を配置します。そして、SynchronousQueueビットのような、しかしそれよりも強力。
14.DelayQueue遅延キューイング
キューに要素が消費者の後に指定された時間遅れで除去することができ、遅延要素は、インタフェースを実装する必要があります。
概要
JAVAや契約におけるコンテナの一部に上記の簡単な紹介は、これらの事を知って、あなたは適切なシーンは既製のものを使用することができたと考えることができますが発生します。私は理由を知りたいし、その後のフォローアップに飛び込むしなければなりませんでした。
最後に、
私たちは、ヨーヨーは、物品、支援への感謝を覚えている賞賛のポイントのように、交換を歓迎します!