回答:
オブジェクトを単にロックしているのであれば、私は 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の使用を避け、必要に応じてa CyclicBarrier
やa などのより高度な同時実行制御を使用することをお勧めしLinkedBlockingQueue
ます。
私が使用する理由だったことがありませんwait()
か、notify()
しかし、いくつかの良いものがあるかもしれませんが。
std::lock_guard
これらのうちどれが実際に優れているのか、なぜですか?
私がいることを発見したLock
とCondition
(および他の新しいconcurrent
クラスが)ツールボックスのためにちょうどより多くのツールがあります。私は古いクローハンマー(synchronized
キーワード)で必要なすべてのことを行うことができましたが、状況によっては使いづらかったです。ツールボックスにツールをさらに追加すると、これらの厄介な状況のいくつかがはるかに簡単になりました。ラバーマレット、ボールピーンハンマー、プライバー、およびいくつかのネイルパンチです。しかし、私の古いクローハンマーはまだその使用のシェアを見ています。
私はどちらか一方が他方より本当に「良い」とは思わないが、どちらもさまざまな問題により適している。簡単に言うと、の単純なモデルとスコープ指向の性質はsynchronized
、コード内のバグから私を保護するのに役立ちますが、これらの同じ利点は、より複雑なシナリオでは妨げになることがあります。対処するためにコンカレントパッケージが作成された、これらのより複雑なシナリオ。ただし、このより高いレベルの構成を使用するには、コードでより明示的かつ注意深い管理が必要です。
===
私は思うのJavaDocは間の区別記述の良い仕事しLock
やがsynchronized
(強調は私です):
ロックの実装は、同期されたメソッドとステートメントを使用して取得できるよりも広範なロック操作を提供します。それらはより柔軟な構造化を可能にし、まったく異なるプロパティを持ち、複数の関連するConditionオブジェクトをサポートするかもしれません。
...
使用synchronizedメソッドや文は、すべてのオブジェクトに関連付けられた暗黙の監視ロックへのアクセスを提供しますが、すべてのロック取得を強制し、ブロック構造の形で発生するリリース:とき、複数のロックがされている取得し、それらは逆の順序で解放されなければならない、とすべてのロックは、それらが取得されたのと同じ字句スコープで解放する必要があります。
同期されたメソッドとステートメントのスコープメカニズムにより、モニターロックを使用したプログラミングがはるかに容易になり、ロックに関連する多くの一般的なプログラミングエラーを回避できますが、より柔軟な方法でロックを操作する必要がある場合があります。たとえば、*いくつかのアルゴリズム*は、同時にアクセスされるデータ構造をトラバースするために、「ハンドオーバーハンド」または「チェーンロック」を使用する必要があります。次に、Bを解放し、Dを取得します。Lockインターフェースの実装は、異なるスコープでロックを取得および解放できるようにすることで、このような手法の使用を可能にします。複数のロックを任意の順序で取得および解放できるようにします。
この柔軟性の向上により、追加の責任が生じます。ブロック構造ロックが存在しないことは、ロックの自動解除除去同期方法及びステートメントで起こります。ほとんどの場合、次のイディオムを使用する必要があります。
...
ときにロックとロック解除が異なるスコープで発生し、世話をするために取られなければならないことを確認ロックが保持されている間に実行されるすべてのコードがあることのtry-最終的またはのtry-catchにより保護されているためにロックが解除されていることを確認し、必要なとき。
ロック実装が提供する追加機能を提供することによって同期方法および文の使用上を取得する非ブロック試行ロック(のtryLock())しようとする試みを遮断することができるロック獲得 lockInterruptiblyを(()、との試み取得タイムアウトする可能性のあるロック(tryLock(long、TimeUnit))。
...
あなたは内のユーティリティすべて達成することができますjava.util.concurrentのは、 のような低レベルのプリミティブで行うsynchronized
、volatile
または待機 / 通知を
ただし、並行性はトリッキーであり、ほとんどの人は少なくともその一部を誤解して、コードを不正確または非効率的(あるいはその両方)にします。
並行APIは、より簡単な(そして安全な)使用方法である、より高レベルのアプローチを提供します。簡単に言えば、synchronized, volatile, wait, notify
もう直接使用する必要はありません。
ロック クラス自体は、直接的に(あなたが使用できることを、あなたも使用する必要がない場合も、このツールボックスの低レベル側にあるQueues
とセマフォやものなど、ほとんどの時間)。
java.util.concurrent
言語機能(synchronized
など)よりも[一般的に]簡単なステートメントをどのようにサポートするかわかりません。を使用するjava.util.concurrent
ときは、lock.lock(); try { ... } finally { lock.unlock() }
コードを書く前に完了する習慣をつける必要がありますが、a を使用すると、synchronized
基本的に最初から問題ありません。これだけを基にしても、私はsynchronized
(その動作が必要であれば)よりも簡単だと思いますjava.util.concurrent.locks.Lock
。パー4
synchronized
またはを使用する主な理由は4つありますjava.util.concurrent.Lock
。
注:組み込みロックとは、同期ロックのことです。
Java 5がReentrantLocksを発表したとき、それらは本質的なロックとはかなり顕著なスループットの違いを持っていることが証明されました。より高速なロックメカニズムを探していて、1.5を実行している場合は、jucReentrantLockを検討してください。Java 6の固有のロックが比較できるようになりました。
jucLockには、ロックのためのさまざまなメカニズムがあります。割り込み可能ロック-ロックしているスレッドが割り込まれるまでロックを試みます。時限ロック-一定時間ロックを試み、成功しない場合はあきらめます。tryLock-他のスレッドがロックを保持している場合は、ロックを試み、中止します。このすべては、単純なロックを除いて含まれています。組み込みロックは単純なロックのみを提供します
バートFの回答にさらにいくつか追加したいと思い ます。
Locks
暗黙的なモニター(synchronized
ロック)よりも表現力のある、より細かいロック制御のためのさまざまな方法をサポートします。
ロックは、共有リソースへの排他的アクセスを提供します。一度に1つのスレッドのみがロックを取得でき、共有リソースへのすべてのアクセスには、最初にロックを取得する必要があります。ただし、ReadWriteLockの読み取りロックなど、一部のロックは共有リソースへの同時アクセスを許可する場合があります。
ドキュメントページからのロックオーバー同期の利点
同期されたメソッドまたはステートメントを使用すると、すべてのオブジェクトに関連付けられている暗黙のモニターロックにアクセスできますが、すべてのロックの取得と解放はブロック構造で発生します。
ロックの実装はlock (tryLock())
、を取得するための非ブロッキングの試み、中断される可能性のあるロックの取得の試み(lockInterruptibly()
および可能性のあるロックの取得の試み)を提供することにより、同期メソッドおよびステートメントの使用に追加の機能を提供しますtimeout (tryLock(long, TimeUnit))
。
Lockクラスは、保証された順序付け、再入不可の使用法、デッドロック検出など、暗黙のモニターロックとはまったく異なる動作とセマンティクスを提供することもできます。
ReentrantLock:私の理解に従って簡単に言うとReentrantLock
、オブジェクトは1つのクリティカルセクションから別のクリティカルセクションに再入することができます。1つのクリティカルセクションに入るロックが既にあるため、現在のロックを使用して、同じオブジェクトの他のクリティカルセクションを作成できます。
ReentrantLock
この記事の主な機能
を使用ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock
して、読み取りおよび書き込み操作の詳細なロックをさらに制御できます 。
これら3つのReentrantLocksとは別に、java 8はもう1つのロックを提供します
StampedLock:
Java 8には、StampedLockと呼ばれる新しい種類のロックが付属しています。これは、上記の例のように、読み取りおよび書き込みロックもサポートします。ReadWriteLockとは対照的に、StampedLockのロックメソッドは、long値で表されるスタンプを返します。
これらのスタンプを使用して、ロックを解除するか、ロックがまだ有効かどうかを確認できます。さらに、スタンプ付きロックは、楽観的ロックと呼ばれる別のロックモードをサポートします。
さまざまなタイプのとロックの使用に関するこの記事ReentrantLock
をStampedLock
ご覧ください。
主な違いは、公平性です。つまり、リクエストはFIFOで処理されますか、それとも割り込みが発生しますか メソッドレベルの同期により、ロックの公平またはFIFO割り当てが保証されます。使用する
synchronized(foo) {
}
または
lock.acquire(); .....lock.release();
公平性は保証されません。
ロックに対して多くの競合がある場合、新しい要求がロックを取得し、古い要求がスタックする割り込みが簡単に発生する可能性があります。ロックのために200個のスレッドが短い順序で到着し、2番目に到着したスレッドが最後に処理された場合を見てきました。これは一部のアプリケーションでは問題ありませんが、他のアプリケーションでは致命的です。
このトピックの詳細については、Brian Goetzの「Java Concurrency In Practice」の本のセクション13.3を参照してください。
ロックは、プログラマーの生活を楽にします。ロックで簡単に実行できるいくつかの状況を次に示します。
一方、ロックと条件は同期メカニズム上に構築されます。したがって、ロックを使用して実現できるのと同じ機能を確実に実現できます。ただし、同期を使用して複雑なシナリオを解決すると、人生が困難になり、実際の問題の解決から逸脱する可能性があります。
ロックと同期ブロックはどちらも同じ目的を果たしますが、使用方法によって異なります。以下の部分を検討してください
void randomFunction(){
.
.
.
synchronize(this){
//do some functionality
}
.
.
.
synchronize(this)
{
// do some functionality
}
} // end of randomFunction
上記の場合、スレッドが同期ブロックに入ると、他のブロックもロックされます。同じオブジェクト上にそのような同期ブロックが複数ある場合、すべてのブロックがロックされます。このような状況では、java.util.concurrent.Lockを使用して、ブロックの不要なロックを防止できます。