ディレクトリ
Javaの楽観的ロックと悲観的ロックにロック
Javaはロック楽観と悲観的ロック、楽観と悲観的ロックに分割しては達成するためによると、実際のロックが、デザインのアイデア、遮断するマルチスレッドのJavaとデータベースの理解のための楽観と悲観的ロックではありません重要なのは、この記事では詳細にこれらの2つのロックと実装の概念を探求します。
ペシミスティック・ロック
悲观锁
悲観的な思考の一種で、常にそれがデータが他の誰かによって変更される可能性が高いと考えて、それが表示されることがあり、最悪の場合を考えて、データが連盟で開催されたので、悲観的ロック资源
または数据
他のスレッドが望んでいるように、ロックされました悲観的なリソースが解放されるまで、それがロックされるまでブロックしますリソース要求。このロック機構の多くを使用する内部従来のリレーショナル・データベース、等行ロック、表ロックなどのロックを読み取り、書き込みロックは、第1の動作を実行する前にロックされています。実現には多くの場合、データベース自体を達成するために悲観的ロックロック機能に依存しています。
JavaではSynchronized
とReentrantLock
にかかわらず、彼らはReetrantLock Synchronziedとリソースを保持するかどうかの、それは誰もが奪われた彼の最愛の赤ちゃんの恐怖のために、ロックしようとするので、その排他ロック(排他ロック)には、また、思考のための悲観的ロックです。
オプティミスティック・ロック
逆に、ロックのアイデアの楽観と悲観的ロックの思想、常にリソースとデータが他の人によって変更されることはありませんので、読み取りがロックされていないだろうと考えられているが、書き込み動作中の楽観的ロックは、現在のデータが変更されたかどうかを判断する時期(具体的には、どのように我々は言って次のことを判断します)。楽観的ロックの実装の一般的な存在の二つは次のとおり版本号机制
とCAS实现
。楽観的ロックとスループットを向上させることができるアプリケーションの多くの種類、より適し。
Javaでjava.util.concurrent.atomic
アトミック変数クラスは、以下のパッケージを実現楽観的ロックCASの一の実施の使用です。
ロックの使用シナリオの他の種類
上記ロックの他の種類の基本的な概念について説明し、適用可能なロックに2つのシナリオをいう、一般的に、悲観的ロックは、悲観的な典型的なコールをロック、読み出し動作のロックに書き込みロックを持っていないであろう。
select * from student where name="cxuan" for update
学生のテーブル名=レコードの「cxuan」とそのロックから選択し、このSQL文は、その後、他の書き込み操作は、これらのデータを操作しないだろうトランザクションを再送信する前に、排他的かつ排他的な役割を果たしました。
読み書きはその性能が比較的低いので、今のところ、インターネットを提唱し、悲観的ロックロックされているので三高
、より一般的に少なくと悲観的ロックを達成し、(高パフォーマンス、高可用性、高同時実行)が、ケースを読んでロックされたものの、性能が比較的低いので、まだ、悲観的ロックを使用する必要がなく、不一致の場合に遭遇ほど楽観的ロックを防止するための再試行時間が書かれています。
これとは対照的に読み、以下の書き込みのため、楽観的ロックの状況、それはほとんど紛争の場面ではありませんので、ロックのオーバーヘッドを保存し、システムのスループットを向上させることができます。
典型的なシステムのコスト、お金の合計に変更を加えるための出納係として、多くは、精度と有効性のデータを確実に使用悲観的ロックは、特定のデータをロックして、他のニーズの変更に対応するためには、該当するシーン楽観的ロックをあります運用データは、この操作は、生成物の量を変更し完了することができない悲惨な瞬間である、楽観的ロック機構のバージョン番号は、この問題を解決することができ、我々は以下を言います。
オプティミスティック・ロックの実装
オプティミスティック・ロック2つの一般的な方法があります採用版本号机制
してCAS(Compare-and-Swap,即比较并替换)算法
実装します。
機構のバージョン番号
バージョン番号が追加データテーブル機構であるversion
達成するためのフィールドを、書き込み動作が行われ、書き込みスレッドAがデータを更新するために、バージョン=バージョン+ 1、成功したときにデータの数は、改変されていることを示し、読み取りちょうど更新は、現在のデータベース内のバージョンと等しい値であるときにバージョンを読み取り、または更新が成功するまで更新を再試行する場合、同時にデータが、更新を送信する時に、バージョン値を読み取ることになります。
上記の例で私たちの金融システムは、簡単にプロセスを説明します。
- システムのコストは、データテーブルを有し、テーブルは2つのフィールドがあり有し
金额
とversion
、リアルタイムの変化量を属性することができるように、バージョンは、各バージョンの変化量を表し、一般的なポリシーであることときの変化量、バージョンごとに+ 1のバージョンに基づいて増分計画。 - 基本的な状況との基本的な情報を理解した後、我々はプロセスを見て:会社が支払いを受けた後、あなたは100ドルが保たれている場合、財源にお金を置く必要があります
- 以下のオープントランザクション:書き込み操作がバックセクションの男性の窓口の前に行われた場合、彼は最初の参照(読み取り)します国庫でどのくらいのお金については、財務省が100元があり、この時点で読んで、あなたは、データベース書き込み操作を実行し、することができます> 1から0から> 120、バージョンバージョン番号 - お金は国庫100から、トランザクションをコミットし、120元にお金を更新しています。
- オープン総務II:賃金の後に従業員になされた要求の女性の窓口レシート、あなたはどのくらいのお金宝庫で、今回のバージョン番号を参照して、国庫から支払われる従業員の給与を除去するための読み取り要求を実行する必要があり、 > 2 - バージョン1から、バージョン+ 1の成功の後、この時間は、トランザクションをコミットします。
上記2つのケースが、その後、並列に実行されるトランザクションは、次に起こるのだろうか?、それは、2つが互いに干渉トランザクションおよびトランザクションであり、上記の二つのトランザクションが順次実行され、最も楽観的なシナリオでありますか
開いているトランザクション、出納最初の読み出し動作を実行するための男、引き出しやバージョン番号、書き込み操作
begin update 表 set 金额 = 120,version = version + 1 where 金额 = 100 and version = 0
この時点で、量は、トランザクションがまだ登録されていません、バージョン番号が1である、120に変更しました
二つのオープン総務、読み出し動作、引き出しとバージョン番号を実行するには、女性の出納係、書き込み操作
begin update 表 set 金额 = 50,version = version + 1 where 金额 = 100 and version = 0
この時点で、量は1、コミットされていないトランザクションを、50にバージョン番号を変更しました
今、トランザクションがコミットされ、量はバージョンが1になり、トランザクションをコミットし、120に変更されました。トランザクションが2成功に提出されることはありませんので、理想的には、それがなるべき額= 50、バージョン= 2、実際に取引を更新2を100とバージョン番号0の量に基づいているが、に基づいており、再度お読みください量およびバージョン番号は、再び書きます。
このように、古いデータに基づいて変更されたバージョン= 0の結果と男性オペレーターの業績をカバーする女性の出納係を回避することができます。
CASアルゴリズム
まずインクリメントとデクリメント後に1000の同時実行の古典的な問題を見てみましょう。
public class Counter {
int count = 0;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public void add(){
count += 1;
}
public void dec(){
count -= 1;
}
}
public class Consumer extends Thread{
Counter counter;
public Consumer(Counter counter){
this.counter = counter;
}
@Override
public void run() {
for(int j = 0;j < Test.LOOP;j++){
counter.dec();
}
}
}
public class Producer extends Thread{
Counter counter;
public Producer(Counter counter){
this.counter = counter;
}
@Override
public void run() {
for(int i = 0;i < Test.LOOP;++i){
counter.add();
}
}
}
public class Test {
final static int LOOP = 1000;
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Producer producer = new Producer(counter);
Consumer consumer = new Consumer(counter);
producer.start();
consumer.start();
producer.join();
consumer.join();
System.out.println(counter.getCount());
}
}
いくつかのテストの結果は、カウントのでそれは、一貫性のないデータの同時実行の問題の出現後に言うことです、0ではありません - = 1、カウント+ = 1が非アトミック操作され、3つのステップを実行するステップは以下のとおりです。
- メモリから読み出された値をカウントし、レジスタにそれを置きます
- + 1つの行ったり - 1つの操作
- 結果の完了の実装と、メモリにコピー
あなたはそれらの原子の証拠がロックされなければならない場合は、使用しSynchronzied
たりReentrantLock
、我々は悲観的ロックを達成するためにそれらを紹介するように、私たちは話している楽観的ロックで、その後、どのような方法でそれのそれらの原子のことを確認するには?読むことを続けてください
CAS compare and swap(比较与交换)
のよく知られたロックフリーのアルゴリズムです。それがブロックされたスレッドが存在しない場合に変数を同期される複数のスレッド間で変数ロック同期を使用せずに達成され、それはまた、非ブロッキング同期(非ブロッキング同期と呼ばれています
CASは、3つの要素が含まれます。
- メモリ値Vを読み書きする必要があります
- 比較値A
- Bは、新しい値を書き込もう
そしてAとVの期待値が同じメモリ値である場合にのみ場合は、メモリ値VはBに修正、または何もしません。
CASのためのJavaサポート:JDK1.5で追加された新しいjava.util.concurrentの(JUC)は、CASの上に構築されています。同期アルゴリズムのためのこの閉塞は、CASは非ブロッキングアルゴリズムです。だから、パフォーマンスのJUCが大幅に改善されました。
私たちは、中java.util.concurrentのAtomicInteger
、たとえば、ロックせずにケースを見て、スレッドセーフを保証する方法です
public class AtomicCounter {
private AtomicInteger integer = new AtomicInteger();
public AtomicInteger getInteger() {
return integer;
}
public void setInteger(AtomicInteger integer) {
this.integer = integer;
}
public void increment(){
integer.incrementAndGet();
}
public void decrement(){
integer.decrementAndGet();
}
}
public class AtomicProducer extends Thread{
private AtomicCounter atomicCounter;
public AtomicProducer(AtomicCounter atomicCounter){
this.atomicCounter = atomicCounter;
}
@Override
public void run() {
for(int j = 0; j < AtomicTest.LOOP; j++) {
System.out.println("producer : " + atomicCounter.getInteger());
atomicCounter.increment();
}
}
}
public class AtomicConsumer extends Thread{
private AtomicCounter atomicCounter;
public AtomicConsumer(AtomicCounter atomicCounter){
this.atomicCounter = atomicCounter;
}
@Override
public void run() {
for(int j = 0; j < AtomicTest.LOOP; j++) {
System.out.println("consumer : " + atomicCounter.getInteger());
atomicCounter.decrement();
}
}
}
public class AtomicTest {
final static int LOOP = 10000;
public static void main(String[] args) throws InterruptedException {
AtomicCounter counter = new AtomicCounter();
AtomicProducer producer = new AtomicProducer(counter);
AtomicConsumer consumer = new AtomicConsumer(counter);
producer.start();
consumer.start();
producer.join();
consumer.join();
System.out.println(counter.getInteger());
}
}
関係なく、循環最終結果は、スレッドの安全性を保証できるのAtomicIntegerを使用して並列に複数のスレッドの場合は0、ある回数、利用できるテストないの後。incrementAndGetとdecrementAndGetはアトミック操作です。この記事では、一時的にそれらが実装されている方法を探求します。
欠点楽観的ロック
何かの長所と短所、ソフトウェア業界は、最善の解決策のための完璧なソリューションであるのみならず、そう楽観的ロックもその弱点や欠点があります。
ABAの問題
ABAは、Aの後、この場合には、考えることができる値が変更されていない問題は、変数の値が最初の読み取りは、ある書き込みをする準備があることが必要であるならば、私は値またはAを見つけたことであると言いますそれは?> B - - この場合は> A、それだけで、それはそれが何であるかを確認するために信じるようにそれを見て、そう思うのAtomicIntegerはありませんそれはAかもしれません。
JDK 1.5の後にAtomicStampedReference
クラスは、能力提供compareAndSet 方法
アトミック全て等しく、このフラグの基準値が設定されている場合に予想される基準と現在のマークに等しい電流基準は、期待されるフラグに等しい場合、最初のチェックであるが更新された値を与えられました。
また、この問題を解決するためにDCAS CASのバリアントを使用することができます。
修飾されたVの数の各々は、基準マーカを増加させるためDCASは、表されています。各V、基準修正一度場合、このカウンタがインクリメントされます。その後も、時間を更新する必要がある変数は、変数やカウンタの値を確認してください。
大きなループのオーバーヘッド
単に短期的に取得され、>再試行メカニズム、この場合は、スピンロックです - 裁判官が成功を書くことができるようになりますとき、私たちは失敗したが、トリガ待ちを書き込む場合、書き込み動作中に楽観的ロックを知っていますかロックを再試行を待っている、それはロックケースを取得していない、長期的には適用されません、より少ないが、加えて、パフォーマンス上のオーバーヘッドのためのスピンサイクルが比較的大きいです。
CASと同期使用シナリオ
単に比較的少数の例を記述するために適用CASの下に置くの下でより多くのケースの書き込みに適用され、同期、(より多くのシーンで、通常はあまり競合読む)(シーンを書き込みし、紛争通常より)
- 少ないリソース(スレッド軽いコンフリクト)の状況に競争、同期同期ロック糸の使用がブロックされ、ユーザーモードとカーネルモードの間に追加のウェイクスイッチング動作がCPUリソースの無駄な消費を切り替え、およびCASハードウェアベースには、カーネルを入力する必要がありません、スレッドを切り替えることなく、スピン操作が少ないチャンスで、より高い性能を得ることができます。
- 状況(重大な紛争スレッド)資源のための深刻な競争のために、CASのスピンの確率は、比較的大きくなるので、より多くのCPUリソースを無駄になり、効率が同期よりも低くなっています。
補足:この領域でのJava並行プログラミングは、synchronizedキーワードのベテランの役割となっている、と長い多くの人々がそれを呼ぶ前に、「ヘビー級のロックを。」しかし、いくつかのケースでは、主に取得するために削減し、ロックがパフォーマンスバイアスされ、ロック、軽量ロックで導入オーバーヘッドとのJava SE 1.6の後、他のさまざまな最適化をもたらす解放するために行わなってからそれほど重くありません。ロックフリーキューは主に同期の実装に依存根底に、基本的な考え方は、スピン閉塞は、公正性を犠牲にし、競争の後に少しロックスイッチを競うし続けますが、高いスループットを得ることです。競合が少ないスレッドの場合、およびCASは、同様の性能を得ることができて、かつ深刻なスレッドの競合状況、パフォーマンスはCASよりもはるかに高いです。
002返信があなたが望むすべてのものを持っている私自身の公開数、男性号へようこそ注意
関連資料:
https://baike.baidu.com/item/悲観的ロック