同時プログラミング(六)-javaのロックはどのように使用しますか?


Javaロックの深さ化
複数のリクエストが同時にデータベースを操作する場合、まず受注ステータスを支払済みに変更し、金額に200を加算すると、同時シーンクエリーの条件下で重複通知が発生します.SQL: Update
悲観ロックと楽観ロック
悲観的なロックは、操作のたびに更新が失われる問題をもたらし、クエリーのたびに排他的なロックを加えると悲観的に考えられています.
データを取りに行くたびに他の人が修正すると思っているので、データを取るたびに鍵をかけます.そうすれば、他の人がこのデータを取りたいと思ってロックを手に入れるまでブロックします.従来のリレーショナル・データベースでは、ロー・ロック、テーブル・ロックなど、リード・ロック、ライト・ロックなど、操作する前に鍵をかけるロック・メカニズムが多く使われています.
Select * from xxx for update;

楽観ロック:楽観ロックは、クエリーのたびに更新が失われないと楽観的に考えられ、バージョンフィールド制御を利用します.
常用楽観ロックフレームワーク
リエントロック
ロックはデータを同時共有し、一貫性を保証するツールとして、JAVAプラットフォームではsynchronizedやReentrantLockなど多くの実装があります.これらのすでに書かれたロックは、私たちの開発に便利を提供しています.
再入ロックは、再帰ロックとも呼ばれ、同じスレッドの外層関数がロックを取得した後も、内層再帰関数はロックを取得するコードを持っているが、影響を受けないことを意味する.
JAVA環境ではReentrantLockもsynchronizedも再ロック可能
public class Test implements Runnable {
	public  synchronized void get() {
		System.out.println("name:" + Thread.currentThread().getName() + " get();");
		set();
	}

	public synchronized  void set() {
		System.out.println("name:" + Thread.currentThread().getName() + " set();");
	}

	@Override

	public void run() {
		get();
	}

	public static void main(String[] args) {
		Test ss = new Test();
		new Thread(ss).start();
		new Thread(ss).start();
		new Thread(ss).start();
		new Thread(ss).start();
	}
}

public class Test02 extends Thread {
	ReentrantLock lock = new ReentrantLock();
	public void get() {
		lock.lock();
		System.out.println(Thread.currentThread().getId());
		set();
		lock.unlock();
	}
	public void set() {
		lock.lock();
		System.out.println(Thread.currentThread().getId());
		lock.unlock();
	}
	@Override
	public void run() {
		get();
	}
	public static void main(String[] args) {
		Test ss = new Test();
		new Thread(ss).start();
		new Thread(ss).start();
		new Thread(ss).start();
	}

}

CASロックレス機構
(1)ロックと比較交換(以下、CASと略す)プログラムは、より複雑に見えるが、非ブロッキング性のため、デッドロック問題に対して天生的に免疫し、スレッド間の相互影響もロックに基づく方式よりはるかに小さい.さらに重要なのは、ロックレス方式を使用するとロック競合によるシステムオーバーヘッドもスレッド間の頻繁なスケジューリングによるオーバーヘッドも全くないため、ロックベースの方法では、より優れたパフォーマンスが得られます.
(2)無ロックの利点:第一に、高合併の場合、ロックされたプログラムよりも優れた性能を有する;第二に、それは生まれつきデッドロック免疫である.
この2つの利点によって、ロックのない同時使用を冒険する価値があります.
(3)CASアルゴリズムのプロセスは、3つのパラメータCAS(V,E,N):Vは更新する変数を表し、Eは予想値を表し、Nは新しい値を表す.V値がE値に等しい場合のみ、Vの値をNに設定します.V値とE値が異なる場合は、他のスレッドが更新されていることを示します.現在のスレッドは何もしません.最後に、CASは現在のVの真の値を返します.
      (4)CAS操作は、常に自分が操作を成功させることができると考えている楽観的な態度で行われる.複数のスレッドが同時にCASを用いて1つの変数を操作すると、1つだけが勝って更新に成功し、残りは失敗する.失敗したスレッドは掛けられず、失敗を告知されただけであり、再試行が許され、もちろん失敗したスレッドが操作を放棄することも許される.このような原理では,CAS動作にロックがなくても,現在のスレッドに対する他のスレッドの干渉を発見し,適切な処理を行うことができる.
(5)簡単に言えば、CASは、この変数が今どのようになっていると思うかという期待値を追加する必要があります.変数があなたが想像していたほどでなければ、他の人に修正されたことを示します.読み直して、もう一度修正してみるといいです.
(6)ハードウェアの面では、ほとんどの現代プロセッサが原子化されたCAS命令をサポートしている.JDK 5.0以降、仮想マシンはこの命令を用いて同時操作と同時データ構造を実現することができ、この操作は仮想マシンにおいてどこにでもあると言える.
/** 
	 * Atomically increments by one the current value. 
	 * 
	 * @return the updated value 
	 */  
	public final int incrementAndGet() {  
	    for (;;) {  
	        //       
	        int current = get();  
	        //       
	        int next = current + 1;  
	        //  Native  compareAndSet,  CAS    
	        if (compareAndSet(current, next))  
	            //          ,        
	            return next;  
	    }  
	}  

スピンロック
スピンロックは、現在のスレッドを循環体内で絶えず実行させることによって実現され、循環の条件が他のスレッドによって変更されると臨界領域に入ることができる.次のように
	private AtomicReference<Thread> sign =new AtomicReference<>();
	public void lock() {
		Thread current = Thread.currentThread();
		while (!sign.compareAndSet(null, current)) {
          }
	}
	public void unlock() {
		Thread current = Thread.currentThread();
		sign.compareAndSet(current, null);
	}

public class Test implements Runnable {
	static int sum;
	private SpinLock lock;

	public Test(SpinLock lock) {
		this.lock = lock;
	}

	/**
	 * @param args
	 * @throws InterruptedException
	 */
	public static void main(String[] args) throws InterruptedException {
		SpinLock lock = new SpinLock();
		for (int i = 0; i < 100; i++) {
			Test test = new Test(lock);
			Thread t = new Thread(test);
			t.start();
		}

		Thread.currentThread().sleep(1000);
		System.out.println(sum);
	}

	@Override
	public void run() {
		this.lock.lock();
		this.lock.lock();
		sum++;
		this.lock.unlock();
		this.lock.unlock();
	}

}

1つのスレッドがこの再入力不可能なスピンロックを呼び出してロックを外す場合は問題ありません.lock()を再び呼び出すと、スピンロックの保持参照が空ではないため、スレッドオブジェクトは他のスレッドがスピンロックを保持していると勘違いします.
CAS原子操作を使用して、lock関数はownerを現在のスレッドに設定し、元の値が空であることを予測します.unlock関数はownerをnullに設定し、予測値は現在のスレッドです.
2番目のスレッドがlock操作を呼び出すと、owner値が空ではないため、最初のスレッドがunlock関数を呼び出してownerをnullに設定するまでループが実行され、2番目のスレッドが臨界領域に入ることができます.
スピンロックは,現在のスレッドをループ体を絶えず実行するだけであり,スレッド状態の変化は行わないため,応答速度がより速くなる.しかし、スレッド数が増加し続けると、各スレッドが実行され、CPU時間がかかるため、パフォーマンスの低下が顕著になる.スレッドの競合が激しくなければ、ロックの期間は維持されます.スピンロックの使用に適しています.
ぶんさんロック
異なるjvmでデータ同期を保証するには、分散ロック技術を使用します.
データベース実装、キャッシュ実装、