セブンイレブン、ロックの深さのJavaのマルチスレッドの基礎

再入可能ロック

1. (例えば、同期(ヘビー)とReentrantLockの(軽量)、等のような)複数の実装におけるツールの一貫性、Javaプラットフォームを確保するために同時共有データとしてロック。これらのロックは、私たちの開発のための利便性を提供するためのプランを書かれています。また、再帰的ロックとして知られているリエントラントロック、ロックを取得するために外側の関数の後に同一のスレッドを参照して、内側の再帰関数は依然としてロックコードを取得しなければならないが、影響を受けません。
ReentrantLockのJava環境で2リエントラントロックを同期しています。

3.コード

//重入锁  轻量级(Lock)与重量级锁(synchronized)---可重入性(递归锁)
public class Test001 implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        set();
    }
    //synchronized代码块执行完毕的时候释放锁
    private synchronized void set() {
        System.out.println("set方法");
        get();
    }
    private synchronized void get() {
        System.out.println("synchronized  可具备可重入性-get方法");
    }
    public static void main(String[] args) {
        Test001 test001 = new Test001();
        Thread thread = new Thread(test001);
        thread.start();
        System.out.println(Thread.currentThread().getName()+"主线程结束");
    }
}

4.結果

main主线程结束
set方法
synchronized  可具备可重入性-get方法

5.コード

//演示lock锁是否具备  可重入性(特征:锁可以传递(方法递归传递)),下面的方法为啥会调两次,因为最后一次调用他已经知道第一次已经上锁了(不会在重新获取锁)
public class Test002 implements Runnable {
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
        set();
    }

    private void set() {
        try {
            //上锁
            lock.lock();
            System.out.println("set方法");
            get();
        } catch (Exception e) {
            //重入锁的目的就是避免死锁
        } finally {
            lock.unlock();//释放锁
        }
    }

    private void get() {
        try {
            lock.lock();
            System.out.println("lock  可具备可重入性-get方法");
        } catch (Exception e) {

        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        Test002 test002 = new Test002();
        Thread thread = new Thread(test002);
        thread.start();
    }
}

6.結果

set方法
lock  可具备可重入性-get方法

第二に、読み書きロック

1.比較するとロック(Javaでロック)でJavaを Lock実装では、読み書きロックはもっと複雑。あなたのプログラムは、共有リソースへの読み取りおよび書き込み操作の数を必要とすると仮定し、読み取りと書き込みの操作はそれほど頻繁ではありません。書き込み動作の不在下で、何の問題もなく、リソースを読み取るための2つのスレッドは、複数のスレッドが同時に共有リソースを読み取ることができる可能にすべきです。これらの共有リソースを書きたい。しかし、スレッドがある場合は、私たちはもはや、読み取りまたは書き込みして他のスレッドにリソースを持つべきではない翻訳者注を(:他の言葉で:読み取りを- 、共存読むことができます-書き込みは共存できない、書き込み-書き込み私たちは)共存できません。これは、この問題を解決するために、読み取り/書き込みロックが必要です。Java5ではjava.util.concurrentパッケージは、すでに読み書きロックが含まれています。それにもかかわらず、我々はその実装の原理を理解する必要があります。

2.コード

//读写锁  jvm内置缓存
public class Test003 {
    private volatile Map<String,String> caChe = new HashMap<>();
    //读写锁
    private ReentrantReadWriteLock  rw1 = new ReentrantReadWriteLock();
    //写入锁
    private WriteLock writeLock = rw1.writeLock();
    //读出锁
    private ReadLock readLock = rw1.readLock();
    //写入元素
    public void put(String key,String value){
        try {
            writeLock.lock();
            System.out.println("正在做写的操作,key:" + key + ",value:" + value + "开始.");
            Thread.sleep(100);
            caChe.put(key,value);
            System.out.println("正在做写的操作,key:" + key + ",value:" + value + "结束.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            writeLock.unlock();
        }
    }
    //读取元素
    public String get(String key){
        try {
            readLock.lock();
            System.out.println("正在做读的操作,key:" + key + ",开始.");
            Thread.sleep(100);
            String value = caChe.get(key);
            System.out.println("正在做读的操作,key:" + key + ",结束.");
            return value;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return null;
        }finally {
            readLock.unlock();
        }
    }

    public static void main(String[] args) {
        Test003 test003 = new Test003();
        //写线程
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0;i<10;i++){
                    test003.put("i",i+"");
                }
            }
        });
        //读线程
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0;i<10;i++){
                    test003.get(i+"");
                }
            }
        });
        t1.start();
        t2.start();
    }
}

3.結果

正在做写的操作,key:i,value:0开始.
正在做写的操作,key:i,value:0结束.
正在做写的操作,key:i,value:1开始.
正在做写的操作,key:i,value:1结束.
正在做读的操作,key:0,开始.
正在做读的操作,key:0,结束.
正在做写的操作,key:i,value:2开始.
正在做写的操作,key:i,value:2结束.
正在做写的操作,key:i,value:3开始.
正在做写的操作,key:i,value:3结束.
正在做写的操作,key:i,value:4开始.
正在做写的操作,key:i,value:4结束.
正在做读的操作,key:1,开始.
正在做读的操作,key:1,结束.
正在做写的操作,key:i,value:5开始.
正在做写的操作,key:i,value:5结束.
正在做写的操作,key:i,value:6开始.
正在做写的操作,key:i,value:6结束.
正在做写的操作,key:i,value:7开始.
正在做写的操作,key:i,value:7结束.
正在做读的操作,key:2,开始.
正在做读的操作,key:2,结束.
正在做写的操作,key:i,value:8开始.
正在做写的操作,key:i,value:8结束.
正在做写的操作,key:i,value:9开始.
正在做写的操作,key:i,value:9结束.
正在做读的操作,key:3,开始.
正在做读的操作,key:3,结束.
正在做读的操作,key:4,开始.
正在做读的操作,key:4,结束.
正在做读的操作,key:5,开始.
正在做读的操作,key:5,结束.
正在做读的操作,key:6,开始.
正在做读的操作,key:6,结束.
正在做读的操作,key:7,开始.
正在做读的操作,key:7,结束.
正在做读的操作,key:8,开始.
正在做读的操作,key:8,结束.
正在做读的操作,key:9,开始.
正在做读的操作,key:9,结束.

第三に、悲観的ロックと楽観的ロック

1.悲観的ロック

常に最悪のケースを想定し、他のすべてのスレッド修正は、データをフェッチするときに、データにアクセスする他のスレッドは、あなたがブロックされてハングアップする必要がある場合、それは、(読み取りロック、書き込みロック、行ロックなど)をロックすると思います。そのような行ロック、読み取りおよび書き込みロック、操作の前にロックされているように、データベースの実装に依存することができ、Javaで、同期の思考は悲観的ロックです。

2.オプティミスティック・ロック

2.1。必ず、データを取得し、データを変更する他のスレッドがないことを信じるたびに何の並行性の問題を考えていない、それはロックされていませんが、更新は別のスレッドを決定する際に何があり、その前にされることはありません達成するためのデータ、通常はバージョン番号またはCAS操作機構を変更します。

 バージョン:通常プラスデータのバージョン番号は、データテーブル内のバージョンフィールドであるデータの数が変更され、データが変更されたとき、プラスバージョン値を表します。データの読み取り中に更新を送信するときに、更新は、バージョン等しい現在のデータベース内の値、または再試行のときだけバージョンを読み取ることと、データ値を更新するスレッドAは、バージョン値を読み取るする場合更新が成功するまで更新。

コアSQL文

更新テーブルセットX = X + 1、バージョン=バージョン+ 1ここで、ID =#{ID}とバージョン=#{バージョン}。    

3つのオペランド、データメモリ値、期待値、新しい値を含む、すなわち、比較およびスワップ、又は比較セット、:CAS操作。ニーズを更新するときに等しい場合にメモリの現在の値が等しい前にリトライが、一般的なスピン動作、すなわち連続的に再試行に失敗した場合、新しい値で更新、値を取るように決定されます。

2.2。例

簡単な例を取るために:と仮定し、データベーステーブル内のアカウント情報は、バージョンフィールド、1の現在の値を有し;フィールドと経常収支(バランス)$ 100。

  1. このとき、(バージョン= 1)を読み出すためのオペレータA、および口座残高から$ 50($ 100- $ 50)を差し引い。
  2. オペレータAの動作中に、オペレータBは、ユーザ情報(バージョン= 1)を読み出し、口座残高から差し引か$図20($ 100- $ 20)です。
  3. データベースの更新にコミット一緒に口座控除バランス(バランス= $ 50)が持つオペレータデータのバージョン番号プラス1(= 2版)の完全な改正は、データ・バージョンを提出するため、この時間は、データベースより大きいデータの現在のバージョンを記録しています更新されたデータベース・レコードは、バージョン2に更新されます。
  4. 操作を完了するためのオペレータBが、また、バージョン番号プラスワン(バージョン= 2)データベースへのデータ(バランス= $ 80)を提出しようとしますが、データベースレコードのバージョンの発見よりも、この時、オペレータBのデータのバージョン番号は、2を提出しました、データベースは、現在のバージョン2を記録し、「バージョンが更新を実行するために、レコードの現在のバージョンよりも大きくなければなりません提出する」楽観的ロック戦略を満たしていないため、オペレータBの提出を拒否されました。

したがって、オペレータAの演算結果のカバーをオペレータBを避けるために古いバージョン= 1つの変更データ結果に基づいていてもよいです。

第四に、の終わり

常に信念を貫きます!!!

公開された122元の記事 ウォン称賛64 ビュー50000 +

おすすめ

転載: blog.csdn.net/chenmingxu438521/article/details/103872814