Java マルチスレッド 07 - JUC 同時実行パッケージ 03

1 同期スレッド ロック、ロック スレッド ロック、および揮発性スレッド ロックの違い

1.1 同期型と揮発型の違い

使用法:

  • volatile キーワードは、複数のスレッド間での変数の可視性の問題を解決します。
  • synchronized キーワードは、複数のスレッド間の共有リソースへのアクセスの同期を解決します。
  • 複数のスレッドが volatile にアクセスする場合、プログラムはブロックされませんが、同期された変更されたメソッドまたはコード ブロックにアクセスする場合はブロックが発生します。
  • volatile は複数のスレッド間での変数の可視性を保証できますが、原子性は保証できません; synchronized はデータ操作の原子性を保証し、データの可視性も間接的に保証でき、スレッド内のプライベート メモリとパブリック メモリのデータを分離します。 。

たとえば、消費にクレジットカードを使用する場合、消費中に銀行がカードを凍結した場合、控除は拒否される必要があります。

現時点では、すべてのスレッドがカードのステータスの変化を認識できる必要があります。そうしないと、ユーザーの損失が発生します。

このステータスをどのスレッドでも見えるようにするには、 を使用して変数​volatile​を。

控除は理解しやすいですが、口座の引き落としアクションがロックされていない場合、口座内の同じ金額が繰り返し消費される可能性があり、銀行に損失が発生します。ロックが追加されると、すべての控除がここで連続的に実行され、アカウントの当座貸越や繰り返しの支払いを避けるために、各消費額が 1 つずつ差し引かれます。

使用するシーン:

  • volatile キーワードは変数を変更する場合にのみ使用できます。
  • synchronized キーワードはメソッドとコード ブロックを変更できます。

1.2 同期とロックの違い

成し遂げる

ロックはインターフェース​synchronized​ですが Java のキーワードであり、組み込み言語によって実装されます。

例外処理メカニズム

synchronized は、例外が発生したときにスレッドが占有しているロックを自動的に解放するため、デッドロックが発生しません。

例外が発生した場合、Lockがunlock()メソッドで積極的にロックを解放しないとデッドロックが発生する可能性が高いため、Lockを使用する場合はfinallyブロックに手動でロックを解放する文を追加する必要があります。

同期{
     语句块; 
ロック

lock = new ReentrantLock() 
lock.lock(); 
施錠開錠();

lock() と lock() はペアで存在する必要があります。

効率

ロックにより、複数のスレッドによる読み取り操作の効率が向上します (読み取り/書き込みロック)。

2 スレッド読み取り/書き込み分離メカニズム

​ReadWriteLock​は読み取り/書き込みロックです

  • 関連するロックのペア「読み取りロック」と「書き込みロック」が維持され、1 つは読み取り操作用で、もう 1 つは書き込み操作用です。
  • 読み取りロックは読み取り専用操作に使用され、共有ロックであり、複数のスレッドによって同時に取得できます。
  • 書き込みロックは書き込み操作に使用されます。これは排他的ロックであり、書き込みロックは 1 つのスレッド ロックによってのみ取得できます。
  • 読み取りロックと書き込みロックは同時に存在できません。読み取り/読み取り操作は同時に実行できますが、読み取り/書き込み操作と書き込み/書き込み操作は同時に実行できません。

2.1 アカウントクラスの作成

public class MyCount { 
    private String id;//账号
    private intCash;//账户余额

    public MyCount(String id, intCash) { 
        this.id = id; 
        this.cash = 現金; 
    public 

    String getId() { 
        ID を返します。
    public void setId(String id) { 
        this.id = id; 
    } 
    } 
    //读取操作
    public int getCash() { 
        System.out.println(Thread.currentThread().getName() + " getcash,Cash=" +Cash); 
        現金を返す。
    } 
    //書き込み操作
    public void setCash(intCash) { 
        System.out.println(Thread.currentThread().getName() + " setcash,Cash=" +Cash);



        this.cash = 現金; 
    } 
}

2.2 ユーザー情報クラスの作成

読み書きロックはユーザー情報クラスで宣言されており、

そして、読み取りメソッドで読み取りロックを作成します。

write メソッドで書き込みロックを作成し、

使用後はすべてのロックを手動で閉める必要があります。

インポート java.util.concurrent.locks.ReadWriteLock; 
import java.util.concurrent.locks.ReentrantReadWriteLock; 

public class User { 
    private 文字列名; 
    プライベート MyCount myCount; 
    //安全读写锁
    private ReadWriteLock readWriteLock; 

    public User(String name, MyCount myCount) { 
        this.name = 名前; 
        this.myCount = myCount; 
        this.readWriteLock = new ReentrantReadWriteLock(); 
    } 

    //残りの
    パブリック void getCash(){ 
        new Thread(){ 
            @Override 
            public void run() { 
                //创建读取锁
                readWriteLock.readLock().lock();
                try { 
                    System.out.println(Thread.currentThread().getName() + " getCash start"); 
                    myCount.getCash(); 
                    Thread.sleep(1000); 
                    System.out.println(Thread.currentThread().getName() + " getCash end"); 
                } catch (InterruptedException e) { 
                    e.printStackTrace(); 
                }finally { 
                    //手アニメーション关闭锁
                    readWriteLock.readLock().unlock(); 
                } 
            } 
        }.start(); 
    } 

    //残りの設定
    public void setCash(final intCash){ 
        new Thread(){
            @Override  
            public void run() {
                //创建書入锁
                readWriteLock.writeLock().lock(); 
                try { 
                    System.out.println(Thread.currentThread().getName() + " setCash start"); 
                    myCount.setCash(現金); 
                    Thread.sleep(1000); 
                    System.out.println(Thread.currentThread().getName() + " setCash end"); 
                } catch (InterruptedException e) { 
                    e.printStackTrace(); 
                }finally { 
                    //手アニメーション关闭锁
                    readWriteLock.writeLock().unlock(); 
                } 
            } 
        }.start();
    } 
}

2.3 テストクラスの作成

テスト クラスでユーザーとアカウントを作成し、マルチスレッドを使用してアカウント情報の読み取りと書き込みを行います。

public class Test { 

    public static void main(String[] args) { 
        //アカウント
        MyCount を作成 myCount = new MyCount("abcd12", 5000); 
        //ユーザーを作成し、アカウントを指定
        User user = new User("Xiao Zhang" , myCount); 

        for (int i = 0; i < 3; i++) { 
            user.getCash(); 
            user.setCash((i + 1) * 1000); 
        } 
    } 
}

2.4 出力結果

スレッド 0 getCash 開始

スレッド 0 getcash、キャッシュ = 5000

スレッド 2 getCash 開始

スレッド 2 getcash、キャッシュ = 5000

スレッド 2 getCash 終了

Thread-0 getCash 終了

スレッド 1 セットキャッシュ スタート

スレッド 1 セットキャッシュ、キャッシュ = 1000

スレッド 1 セットキャッシュエンド

スレッド 4 getCash 開始

スレッド 4 getcash、キャッシュ = 1000

スレッド 4 getCash 終了

スレッド 5 セットキャッシュ スタート

スレッド 5 セットキャッシュ、キャッシュ = 3000

スレッド 5 セットキャッシュエンド

スレッド 3 セットキャッシュ スタート

スレッド 3 セットキャッシュ、キャッシュ = 2000

スレッド 3 セットキャッシュエンド

出力結果では、次のことがわかります。

読み取り操作 getCash は Thread-0 で実行されますが、それが完了する前に Thread-2 も同時に読み取り操作に入り、それらは並行して実行されます

書き込み操作 setCash は、最初から最後まですべてのスレッドが実行され、途中で他のスレッドが入らない、排他的実行です。

おすすめ

転載: blog.csdn.net/QQ156881887/article/details/129100665