web-dev-qa-db-ja.com

同期とロック

Java.util.concurrent APIは、Lockと呼ばれるクラスを提供します。これは基本的に、重要なリソースにアクセスするためにコントロールをシリアル化します。 park()unpark()などのメソッドを提供します。

synchronizedキーワードを使用し、wait()およびnotify() notifyAll()メソッドを使用できる場合、同様のことができます。

これらのどれが実際に優れているのか、なぜだろうか?

168
daydreamer

オブジェクトを単純にロックする場合は、synchronizedを使用することをお勧めします

例:

Lock.acquire();
doSomethingNifty(); // Throws a NPE!
Lock.release(); // Oh noes, we never release the lock!

どこでもtry{} finally{}を明示的に行う必要があります。

同期では、間違いを犯すことは非常に明確で不可能です。

synchronized(myObject) {
    doSomethingNifty();
}

そうは言っても、Locksは、このようなクリーンな方法で取得およびリリースできない、より複雑なものにはより役立つ場合があります。正直なところ、最初は裸のLocksの使用を避け、必要に応じてCyclicBarrierLinkedBlockingQueueなどのより高度な同時実行制御を使用することをお勧めします。

wait()またはnotify()を使用する理由は一度もありませんが、良いものがあるかもしれません。

170

Java.util.concurrent のユーティリティが、synchronizedvolatile、または wait / notify のような低レベルのプリミティブで行うすべてを実現できます。

ただし、同時実行は注意が必要であり、ほとんどの人は少なくともその一部を誤ってしまい、コードが正しくないか非効率的(またはその両方)になります。

並行APIは、より簡単な(そしてより安全な)使用が可能な高レベルのアプローチを提供します。簡単に言えば、synchronized, volatile, wait, notifyを直接使用する必要はもうないはずです。

Lock クラス自体はこのツールボックスの下位レベル側にあり、直接使用する必要がない場合もあります(Queuesおよび Semaphore などを使用できます) 、 ほとんどの時間)。

20
Thilo

synchronizedまたはJava.util.concurrent.Lockを使用する理由には、主に4つの要因があります。

注:同期ロックとは、固有ロックと言うときの意味です。

  1. Java 5がReentrantLocksとともに出てきたとき、それらは本質的なロックとはかなり顕著なスループットの違いがあることが証明されました。より高速なロックメカニズムを探していて、1.5を実行している場合は、j.u.c.ReentrantLockを検討してください。 Java 6の固有のロックは比較可能になりました。

  2. j.u.c.Lockにはロックのためのさまざまなメカニズムがあります。割り込み可能ロック-ロックスレッドが中断されるまでロックを試行します。時限ロック-一定時間ロックを試み、成功しない場合はあきらめます。 tryLock-他のスレッドがロックを保持している場合、ロックを試みます。これはすべて、単純なロックとは別に含まれています。固有のロックは単純なロックのみを提供します

  3. スタイル。 1と2の両方が、私を含むほとんどの人が関心を持っているもののカテゴリーに分類されない場合、j.u.c。Lockのロックよりも固有のロックの精液が読みやすく、冗長性が低いことに気づきます。
  4. 複数の条件。ロックするオブジェクトに通知することができ、単一のケースを待つことができます。 LockのnewConditionメソッドを使用すると、単一のLockで複数の理由で待機またはシグナルを送信できます。実際にこの機能を実際に必要とすることはありませんが、それを必要とする人にとっては素晴らしい機能です。
13
John Vint

Bert F answerの上にさらにいくつか追加したいと思います。

Locksは、暗黙のモニター(synchronizedロック)よりも表現力のある、よりきめ細かいロック制御のためのさまざまなメソッドをサポートします。

ロックは共有リソースへの排他的アクセスを提供します。一度に1つのスレッドのみがロックを取得でき、共有リソースへのすべてのアクセスにはロックを最初に取得する必要があります。ただし、一部のロックでは、ReadWriteLockの読み取りロックなど、共有リソースへの同時アクセスが許可される場合があります。

ドキュメントのLock over Synchronizationの利点 page

  1. 同期化されたメソッドまたはステートメントを使用すると、すべてのオブジェクトに関連付けられた暗黙的なモニターロックへのアクセスが提供されますが、すべてのロックの取得と解放はブロック構造の方法で強制されます

  2. ロックの実装は、lock (tryLock())を取得するための非ブロッキング試行、中断される可能性のあるロックの取得試行(lockInterruptibly()、およびtimeout (tryLock(long, TimeUnit))を取得できるロックの取得試行)を提供することにより、同期メソッドおよびステートメントの使用を超える追加機能を提供します。

  3. Lockクラスは、順序の保証、再入不可の使用、デッドロックの検出など、暗黙的なモニターロックとはまったく異なる動作とセマンティクスも提供できます

ReentrantLock :私の理解によると、ReentrantLockを使用すると、あるクリティカルセクションから別のクリティカルセクションにオブジェクトを再入力できます。 1つのクリティカルセクションに入るためのロックがすでにあるため、現在のロックを使用して同じオブジェクトの他のクリティカルセクションを作成できます。

ReentrantLockこのように重要な機能 記事

  1. 割り込み可能にロックする機能。
  2. ロック待機中にタイムアウトする機能。
  3. 公正なロックを作成する力。
  4. ロックを待機しているスレッドのリストを取得するAPI。
  5. ブロックせずにロックを試行する柔軟性。

ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLockを使用して、読み取りおよび書き込み操作の詳細ロックの制御をさらに取得できます。

これら3つのReentrantLocksとは別に、Java 8はもう1つのロックを提供します

StampedLock:

Java 8には、StampedLockと呼ばれる新しい種類のロックが付属しており、上記の例と同様に読み取りおよび書き込みロックもサポートしています。 ReadWriteLockとは対照的に、StampedLockのロックメソッドは、長い値で表されるスタンプを返します。

これらのスタンプを使用して、ロックを解除するか、ロックがまだ有効かどうかを確認できます。さらに、スタンプ付きロックは、オプティミスティックロックと呼ばれる別のロックモードをサポートしています。

異なるタイプのReentrantLockおよびStampedLockロックの使用法については、これをご覧ください 記事 .

5
Ravindra babu

主な違いは公平性です。言い換えると、リクエストはFIFOで処理されますか、それとも割り込みがありますか?メソッドレベルの同期により、ロックの公平またはFIFO割り当てが保証されます。を使用して

synchronized(foo) {
}

または

lock.acquire(); .....lock.release();

公平性を保証するものではありません。

ロックに多くの競合がある場合、新しいリクエストがロックを取得し、古いリクエストがスタックするバージに簡単に遭遇する可能性があります。ロックのために200個のスレッドが短時間で到着し、2番目に到着するスレッドが最後に処理される場合があります。これは一部のアプリケーションでは問題ありませんが、他のアプリケーションでは致命的です。

このトピックの詳細については、Brian Goetzの「Java Concurrency In Practice」のセクション13.3を参照してください。

4
Brian Tarbox

Brian Goetzの「Java Concurrency In Practice」ブック、セクション13.3:「...デフォルトのReentrantLockと同様に、組み込みロックは確定的な公平性保証を提供しませんが、ほとんどのロック実装の統計的公平性保証はほとんどすべての状況で十分です...」

3
bodrin

ロックはプログラマーの生活を楽にします。ロックを使用すると簡単に達成できる状況はほとんどありません。

  1. 1つの方法でロックし、他の方法でロックを解除します。
  2. 2つの異なるコードの部分で動作する2つのスレッドがありますが、最初のスレッドは2番目のスレッドに依存して特定のコードの部分を完了してから先に進みます(他のスレッドも同時に動作します)。共有ロックは、この問題を非常に簡単に解決できます。
  3. モニターの実装。たとえば、putメソッドとgetメソッドが多くの異なるスレッドから実行される単純なキュー。ただし、同じメソッドを互いにオーバーラップさせたり、putメソッドとgetメソッドの両方をオーバーラップさせたりすることはできません。そのような場合、プライベートロックは非常に簡単です。

一方、ロックと条件は同期されたものに基づいています。ですから、確かに同じ目標を達成できます。しかし、それはあなたの人生を困難にし、実際の問題の解決からあなたを逸らす可能性があります。

1

ロックと同期の主な違い:

  • ロックを使用すると、任意の順序でロックを解除および取得できます。
  • 同期を使用すると、取得した順序でのみロックを解除できます。
0
NKumar

ブロックのロックと同期はどちらも同じ目的を果たしますが、使用法によって異なります。以下の部分を検討してください

void randomFunction(){
.
.
.
synchronize(this){
//do some functionality
}

.
.
.
synchronize(this)
{
// do some functionality
}


} // end of randomFunction

上記の場合、スレッドが同期ブロックに入ると、他のブロックもロックされます。同じオブジェクトにこのような同期ブロックが複数ある場合、すべてのブロックがロックされます。このような状況では、Java.util.concurrent.Lockを使用して、ブロックの不要なロックを防ぐことができます

0
Shyam