高度なマルチスレッド制御クラス
Java1.5 は、非常に効率的で実用的なマルチスレッド パッケージ java.util.concurrent を提供します。これは、開発者が効率的で保守が容易で、明確に構造化された Java マルチスレッド プログラムを作成するのに役立つ高度なツールを多数提供します。
ThreadLocal クラス
ThreadLocal クラスは、スレッドの独立変数を保存するために使用されます。スレッドクラスの場合(Threadから継承)
ThreadLocal を使用して変数を管理する場合、ThreadLocal は変数を使用するスレッドごとに変数の独立したコピーを提供するため、各スレッドは他のスレッドの対応するコピーに影響を与えることなく、独自のコピーを独立して変更できます。セッション情報の記録など、ユーザーのログイン制御に一般的に使用されます。
実装: 各スレッドは、TreadLocalMap 型の変数を保持します (このクラスは軽量の Map であり、機能はマップと同じですが、違いは、エントリがエントリのリンクされたリストではなくバケットに配置されることです。関数は依然としてマップです) .) それ自体がキーであり、ターゲットが値です。
主なメソッドは get() と set(T a) で、set 後は、map 内に threadLocal -> a が保持され、取得時に a が返されます。ThreadLocal は特別なコンテナです。
アトミック クラス (AtomicInteger、AtomicBoolean...)
atomicInteger などのアトミック ラッパー クラスを使用する場合、または独自の保証されたアトミック操作を使用する場合は、同期化されたものと同等です。
AtomicInteger.compareAndSet(int Expect,int update)//戻り値はブール値です
アトミックリファレンス
AtomicReference の場合、オブジェクトのプロパティが失われる可能性があります。つまり、oldObject == current ですが、oldObject.getPropertyA != current.getPropertyA になります。
このとき、AtomicStampedReference が役に立ちます。これも非常に一般的なアイデアです。つまり、バージョン番号を追加します。
ロッククラス
lock: java.util.concurrent パッケージ内。次の 3 つの実装があります。
-
リエントラントロック
-
ReentrantReadWriteLock.ReadLock
-
再入可能ReadWriteLock.WriteLock
主な目的は synchronized と同じで、どちらも同期の問題を解決し、リソースの紛争を処理するために作成されたテクノロジです。機能は似ていますが、いくつかの違いがあります。
違いは次のとおりです。
-
ロックはより柔軟であり、複数のロックのシャックルを解除する順序を自由に定義できます (同期を最初に追加してからロックを解除する必要があります)
-
さまざまなロック スキーム、ロック ブロッキング、trylock ノンブロッキング、lockInterruptly 割り込み可能、およびタイムアウト付き trylock バージョンを提供します。
-
基本的にモニターロックと同じ(つまり同期)
-
大きな力には大きな責任が伴い、ロックとロック解除を制御する必要があります。そうしないと、災害が発生する可能性があります。
-
Condition クラスとの組み合わせ。
-
以下に示すように、より高いパフォーマンス、同期とロックのパフォーマンスの比較:
リエントラントロックの使用
再入可能性の意味は、ロックを保持しているスレッドがロックを保持し続けることができ、ロックが実際に解放される前に同じ回数だけロックを解放する必要があることです。
private java.util.concurrent.locks.Lock lock = new ReentrantLock(); public void method() { try { lock.lock(); //ロック、同期ブロックを取得 }finally { lock.unlock();/ /releaseロック } }
-
ReentrantLock は同期ロックよりも強力であり、主に次の点に反映されます。
-
ReentrantLock には公平な戦略を選択できます。
-
ReentrantLock はロック取得時に条件付きで取得でき、待ち時間を設定できるため、デッドロックを効果的に回避できます。
-
tryLock() や tryLock(long timeout, TimeUnit 単位) など
-
ReentrantLock は、ロックに関するさまざまな情報を取得でき、ロックのさまざまな状態を監視するために使用されます。
-
ReentrantLockは複数の通知、つまりConditionの適用を柔軟に実装できます。
公平なロックと不公平なロック
ReentrantLock のデフォルトは不公平なロックであり、スレッドが「キューをプリエンプト」してロックを取得できるようになります。公平なロックとは、FIFO 戦略と同様に、スレッドが要求された順序でロックを取得することを意味します。
ロックの使用
-
lock() はブロック方式でロックを取得し、ロックを取得した後にのみ割り込み情報を処理します。
-
lockInterruptibly() はブロック的にロックを取得し、割り込み情報を即座に処理し、例外をスローします。
-
tryLock() はロックの取得を試みます。成功または失敗に関係なく、すぐに true または false を返します。ロックが公平な並べ替え戦略を使用するように設定されている場合でも、tryLock() は公平性をオンにしてジャンプできることに注意してください。キューをプリエンプトします。このロックの公平な設定を尊重したい場合は、ほぼ同等の tryLock(0, TimeUnit.SECONDS) を使用してください (割り込みも検出します)。
-
tryLock(long timeout, TimeUnit Unit) は、タイムアウト時間内にブロック方式でロックを取得し、正常に true を返し、タイムアウト時に false を返し、すぐに割り込み情報を処理して例外をスローします。
公平なロックへの侵入を可能にする時間指定された tryLock を使用したい場合は、時間指定されたフォームと時間指定されていないフォームを組み合わせることができます。
if (lock.tryLock() || lock.tryLock(タイムアウト, ユニット) ) { ... }
private java.util.concurrent.locks.ReentrantLock lock = new ReentrantLock(); public void testMethod() { try { if (lock.tryLock(1, TimeUnit.SECONDS)) { //ロック、同期ブロックを取得 } else { / /ロックは取得されませんでした } } catch (InterruptedException e) { e.printStackTrace(); }finally { if (lock.isHeldByCurrentThread()) //現在のスレッドがロックを保持している場合は、ロックを解放します lock.unlock (); } }
条件の使用
ロックによって条件を作成し、マルチチャネル通知メカニズムを実装できます。
await、signal、および signalAll を含むメソッドは wait/notify に似ており、ロックを取得した後に呼び出す必要があります。
プライベート最終 java.util.concurrent.locks.Lock ロック = new ReentrantLock(); プライベート最終 java.util.concurrent.locks.Condition 条件 = lock.newCondition(); パブリック void await() { try { lock.lock() ; //ロックの取得 condition.await(); //コンディション通信信号を待ち、コンディションロックを解除 //コンディション通信を受信 } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock() ;/ /オブジェクトのロックを解除します } }
ReentrantReadWriteLock の使用
ReentrantReadWriteLock は、ReentrantLock をさらに拡張したもので、読み取りロック readLock() (共有ロック) と書き込みロック writeLock() (排他ロック) を実装し、読み取りと書き込みの分離を実現します。読み取りと読み取りは相互に排他的ではなく、読み取りと書き込み、書き込みと読み取り、書き込みと書き込みのみが相互排他的であるため、読み取りと書き込みのパフォーマンスが向上します。
読み取りロックの例:
private Final java.util.concurrent.locks.ReadWriteLock lock = new ReentrantReadWriteLock(); public void method() { try { lock.readLock().lock();//読み取りロックを取得 readLock、同期されたブロック }finally { lock 。 readLock().unlock();//読み取りロックを解除します readLock } }
書き込みロックの例:
private Final java.util.concurrent.locks.ReadWriteLock lock = new ReentrantReadWriteLock(); public void method() { try { lock.writeLock().lock(); //書き込みロックを取得 writeLock、同期されたブロック }finally { lock . writeLock().unlock(); //書き込みロックを解除します writeLock } }
コンテナクラス
同期コンテナと非同期コンテナの概要
コンテナを同期する
次の 2 つの部分が含まれます。
-
1 つは初期の JDK の Vector と Hashtable です。
-
1 つは、これと同種のコンテナである、JDK1.2 で追加された同期ラッパー クラスで、Collections.synchronizedXxx ファクトリ メソッドを使用して作成されます。
Map<String, Integer> hashmapSync = Collections.synchronizedMap(new HashMap<>());
同期コンテナはスレッドセーフであり、一度に 1 つのスレッドのみがコンテナの状態にアクセスできます。
ただし、シナリオによっては、複合操作を保護するためにロックが必要になる場合があります。
追加、削除、反復、ジャンプ、条件付き操作などの複合クラス操作。
これらの複合操作は、複数のスレッドがコンテナを同時に変更すると、予期しない動作を示す可能性があります。
最も古典的なものは ConcurrentModificationException です。
その理由は、コンテナーが反復されると、コンテンツが同時に変更されるためです。これは、初期のイテレーター設計では同時変更の問題が考慮されていなかったためです。
基礎となるメカニズムは、従来の synchronized キーワードを使用して各パブリック メソッドを同期することに他ならないため、一度に 1 つのスレッドのみがコンテナの状態にアクセスできます。これは明らかに、今日のインターネット時代の高い同時実行性のニーズを満たしておらず、スレッドの安全性を確保しながら、十分なパフォーマンスも備えている必要があります。
同時コンテナ
Collections.synchronizedXxx() 同期コンテナーなどと比較して、util.concurrent で導入された並行コンテナーは主に次の 2 つの問題を解決します。
-
特定のシナリオに従って設計し、同期を回避して同時実行性を提供するように努めます。
-
いくつかの同時実行安全な複合操作を定義し、同時環境での反復操作が間違っていないことを保証します。
util.concurrent のコンテナーが反復する場合、同期してカプセル化する必要はなく、例外がスローされないことを保証できますが、毎回表示される「最新かつ最新の」データではない可能性があります。
Map<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();
ConcurrentHashMap は、同期されたマップ (Collections.synchronized(new HashMap())) を置き換えます。
ご存知のとおり、HashMap はハッシュ値に基づいてセグメントに保存されます。同期マップは同期中にマップ全体をロックしますが、ConcurrentHashMap はストレージの設計時にセグメント定義を導入します。同期中は、ハッシュ値に基づいてロックするだけで済みます。ハッシュ値が配置されている段落。これにより、パフォーマンスが大幅に向上します。ConcurrentHashMap は、「追加されていない場合」: putIfAbsent()、置換: replace() などの一般的な複合操作のサポートも追加します。これら 2 つの操作はどちらもアトミック操作です。ConcurrentHashMap は size() メソッドと isEmpty() メソッドを弱め、ロックの可能性を避けるために同時状況ではそれらの使用をできる限り少なくすることに注意してください (もちろん、マップの数が制限されない場合は、ロックせずに値を取得することも可能です)変化)。
CopyOnWriteArrayList と CopyOnWriteArraySet は、それぞれ List と Set を置き換えます。これらは主に、トラバーサル操作が主な操作である場合に、同期 List と同期 Set を置き換えるために使用されます。これは、上記の考え方です。反復プロセスでは、エラーが発生しないようにする必要があります。ロックに加えて、もう 1 つの方法は、コンテナ オブジェクトを「複製」することです。---欠点も明らかです。メモリを占有し、データは最終的に一貫性がありますが、リアルタイムではデータの一貫性が保てない可能性があります。一般に、読み取りが多く書き込みが少ない同時シナリオで使用されます。
-
ConcurrentSkipListMap は、効率的な同時実行で SoredMap を置き換えることができます (Collections.synchronzedMap でラップされた TreeMap など)。
-
ConcurrentSkipListSet は、効率的な同時実行で SoredSet を置き換えることができます (Collections.synchronzedSet でラップされた TreeMap など)。
-
ConcurrentLinkedQuerue は先入れ先出しキューです。ノンブロッキングキューです。size() の代わりに isEmpty を使用するように注意してください。
CountDownLatch ラッチの使用
管理
管理クラスの概念は比較的一般的であり、スレッドの管理に使用されます。それ自体はマルチスレッドではありませんが、上記のツールを使用してカプセル化を行うためのいくつかのメカニズムを提供します。
私が学んだ、言及する価値のある管理クラス: ThreadPoolExecutor と、JMX フレームワークのシステムレベル管理クラス ThreadMXBean
スレッドプールエグゼキュータ
このクラスを知らない場合は、前に説明した ExecutorService について知っておくとよいですが、独自のスレッド プールを開くと非常に便利です。
ExecutorService e = Executors.newCachedThreadPool(); ExecutorService e = Executors.newSingleThreadExecutor(); ExecutorService e = Executors.newFixedThreadPool(3); // 1 つ目は可変サイズのスレッド プールで、タスクの数に応じてスレッドを割り当てます。 // 2 番目はシングルスレッド プールであり、FixedThreadPool(1) と同等です。 // 3 番目は固定サイズのスレッド プールです。 // 次に e.execute(new MyRunnableImpl()) を実行します。
このクラスは ThreadPoolExecutor を通じて内部的に実装されています。このクラスをマスターすると、スレッド プールの管理を理解するのに役立ちます。本質的に、これらはすべて ThreadPoolExecutor クラスのさまざまな実装バージョンです。
参考記事:
Java マルチスレッド同時プログラミングの概要トランスクリプト https://www.cnblogs.com/yw0219/p/10597041.html
Java でのマルチスレッドについては、この記事を読むだけで済みます https://juejin.im/entry/57339fe82e958a0066bf284f
記事「Java 同時プログラミング (2): Java マルチスレッド - java.util.concurrent 高度なツール」を転載します。
出典を示してください: java 並行プログラミング (2): Java マルチスレッド - java.util.concurrent 高度なツール - java レビューに重点を置くコアコンセプトの内容と詳細な説明 - Zhou Junjun の個人 Web サイト