Java.util.concurrent
APIは、Lock
と呼ばれるクラスを提供します。これは基本的に、重要なリソースにアクセスするためにコントロールをシリアル化します。 park()
やunpark()
などのメソッドを提供します。
synchronized
キーワードを使用し、wait()
およびnotify() notifyAll()
メソッドを使用できる場合、同様のことができます。
これらのどれが実際に優れているのか、なぜだろうか?
オブジェクトを単純にロックする場合は、synchronized
を使用することをお勧めします
例:
Lock.acquire();
doSomethingNifty(); // Throws a NPE!
Lock.release(); // Oh noes, we never release the lock!
どこでもtry{} finally{}
を明示的に行う必要があります。
同期では、間違いを犯すことは非常に明確で不可能です。
synchronized(myObject) {
doSomethingNifty();
}
そうは言っても、Lock
sは、このようなクリーンな方法で取得およびリリースできない、より複雑なものにはより役立つ場合があります。正直なところ、最初は裸のLock
sの使用を避け、必要に応じてCyclicBarrier
やLinkedBlockingQueue
などのより高度な同時実行制御を使用することをお勧めします。
wait()
またはnotify()
を使用する理由は一度もありませんが、良いものがあるかもしれません。
Java.util.concurrent のユーティリティが、synchronized
、volatile
、または wait / notify のような低レベルのプリミティブで行うすべてを実現できます。
ただし、同時実行は注意が必要であり、ほとんどの人は少なくともその一部を誤ってしまい、コードが正しくないか非効率的(またはその両方)になります。
並行APIは、より簡単な(そしてより安全な)使用が可能な高レベルのアプローチを提供します。簡単に言えば、synchronized, volatile, wait, notify
を直接使用する必要はもうないはずです。
Lock クラス自体はこのツールボックスの下位レベル側にあり、直接使用する必要がない場合もあります(Queues
および Semaphore などを使用できます) 、 ほとんどの時間)。
synchronized
またはJava.util.concurrent.Lock
を使用する理由には、主に4つの要因があります。
注:同期ロックとは、固有ロックと言うときの意味です。
Java 5がReentrantLocksとともに出てきたとき、それらは本質的なロックとはかなり顕著なスループットの違いがあることが証明されました。より高速なロックメカニズムを探していて、1.5を実行している場合は、j.u.c.ReentrantLockを検討してください。 Java 6の固有のロックは比較可能になりました。
j.u.c.Lockにはロックのためのさまざまなメカニズムがあります。割り込み可能ロック-ロックスレッドが中断されるまでロックを試行します。時限ロック-一定時間ロックを試み、成功しない場合はあきらめます。 tryLock-他のスレッドがロックを保持している場合、ロックを試みます。これはすべて、単純なロックとは別に含まれています。固有のロックは単純なロックのみを提供します
Bert F answerの上にさらにいくつか追加したいと思います。
Locks
は、暗黙のモニター(synchronized
ロック)よりも表現力のある、よりきめ細かいロック制御のためのさまざまなメソッドをサポートします。
ロックは共有リソースへの排他的アクセスを提供します。一度に1つのスレッドのみがロックを取得でき、共有リソースへのすべてのアクセスにはロックを最初に取得する必要があります。ただし、一部のロックでは、ReadWriteLockの読み取りロックなど、共有リソースへの同時アクセスが許可される場合があります。
ドキュメントのLock over Synchronizationの利点 page
同期化されたメソッドまたはステートメントを使用すると、すべてのオブジェクトに関連付けられた暗黙的なモニターロックへのアクセスが提供されますが、すべてのロックの取得と解放はブロック構造の方法で強制されます
ロックの実装は、lock (tryLock())
を取得するための非ブロッキング試行、中断される可能性のあるロックの取得試行(lockInterruptibly()
、およびtimeout (tryLock(long, TimeUnit))
を取得できるロックの取得試行)を提供することにより、同期メソッドおよびステートメントの使用を超える追加機能を提供します。
Lockクラスは、順序の保証、再入不可の使用、デッドロックの検出など、暗黙的なモニターロックとはまったく異なる動作とセマンティクスも提供できます
ReentrantLock :私の理解によると、ReentrantLock
を使用すると、あるクリティカルセクションから別のクリティカルセクションにオブジェクトを再入力できます。 1つのクリティカルセクションに入るためのロックがすでにあるため、現在のロックを使用して同じオブジェクトの他のクリティカルセクションを作成できます。
ReentrantLock
このように重要な機能 記事
ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock
を使用して、読み取りおよび書き込み操作の詳細ロックの制御をさらに取得できます。
これら3つのReentrantLocksとは別に、Java 8はもう1つのロックを提供します
StampedLock:
Java 8には、StampedLockと呼ばれる新しい種類のロックが付属しており、上記の例と同様に読み取りおよび書き込みロックもサポートしています。 ReadWriteLockとは対照的に、StampedLockのロックメソッドは、長い値で表されるスタンプを返します。
これらのスタンプを使用して、ロックを解除するか、ロックがまだ有効かどうかを確認できます。さらに、スタンプ付きロックは、オプティミスティックロックと呼ばれる別のロックモードをサポートしています。
異なるタイプのReentrantLock
およびStampedLock
ロックの使用法については、これをご覧ください 記事 .
主な違いは公平性です。言い換えると、リクエストはFIFOで処理されますか、それとも割り込みがありますか?メソッドレベルの同期により、ロックの公平またはFIFO割り当てが保証されます。を使用して
synchronized(foo) {
}
または
lock.acquire(); .....lock.release();
公平性を保証するものではありません。
ロックに多くの競合がある場合、新しいリクエストがロックを取得し、古いリクエストがスタックするバージに簡単に遭遇する可能性があります。ロックのために200個のスレッドが短時間で到着し、2番目に到着するスレッドが最後に処理される場合があります。これは一部のアプリケーションでは問題ありませんが、他のアプリケーションでは致命的です。
このトピックの詳細については、Brian Goetzの「Java Concurrency In Practice」のセクション13.3を参照してください。
Brian Goetzの「Java Concurrency In Practice」ブック、セクション13.3:「...デフォルトのReentrantLockと同様に、組み込みロックは確定的な公平性保証を提供しませんが、ほとんどのロック実装の統計的公平性保証はほとんどすべての状況で十分です...」
ロックはプログラマーの生活を楽にします。ロックを使用すると簡単に達成できる状況はほとんどありません。
一方、ロックと条件は同期されたものに基づいています。ですから、確かに同じ目標を達成できます。しかし、それはあなたの人生を困難にし、実際の問題の解決からあなたを逸らす可能性があります。
ロックと同期の主な違い:
ブロックのロックと同期はどちらも同じ目的を果たしますが、使用法によって異なります。以下の部分を検討してください
void randomFunction(){
.
.
.
synchronize(this){
//do some functionality
}
.
.
.
synchronize(this)
{
// do some functionality
}
} // end of randomFunction
上記の場合、スレッドが同期ブロックに入ると、他のブロックもロックされます。同じオブジェクトにこのような同期ブロックが複数ある場合、すべてのブロックがロックされます。このような状況では、Java.util.concurrent.Lockを使用して、ブロックの不要なロックを防ぐことができます