2.オブジェクトと変数の同時アクセス

6377 ワード

スレッドセキュリティ:取得したインスタンス変数の値は同期処理されており、汚れた読み取りは発生しません.
≪非スレッド・セキュリティ|Non-Thread Security|oem_src≫:同じオブジェクト内のインスタンス変数に複数のスレッドが同時アクセスすると発生し、結果としてダーティ・リードが発生します.

1.synchronized同期方法


1.1メソッド内の変数はスレッドセキュリティ


メソッド内部で宣言された変数は、スレッドの安全な変数です.変数のプライベート特性によるものです.
public class HasSelfPrivateNum {
    public void addI(String username) {
        try {
            int num = 0;
            if (username.equals("a")) {
                num = 100;
                System.out.println(" a set over!");
                Thread.sleep(2000);
            } else {
                num = 200;
                System.out.println(" b set over!");
            }
            System.out.println(String.format("%s num=%s",username,num));
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}
  • 出力結果
  • a set over!
    b set over!
    b num=200
    a num=100
    

    1.2インスタンス変数非スレッドセキュリティ


    複数のスレッドが同じオブジェクトのインスタンス変数に共通してアクセスしている場合、非スレッドセキュリティの問題が発生します.
    上記のnumプライベート変数をインスタンス変数にすると、numは非スレッドで安全になります.
    public class HasSelfPrivateNum {
        private int num = 0;
    
        public void addI(String username) {
            try {
                //int num = 0;
                if (username.equals("a")) {
                    num = 100;
                    System.out.println("a set over!");
                    Thread.sleep(2000);
                } else {
                    num = 200;
                    System.out.println("b set over!");
                }
                System.out.println(String.format("%s num=%s", username, num));
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }
    
  • 出力結果
  • a set over!
    b set over!
    b num=200
    a num=200
    
  • ソリューション
  • メソッドの前にsyncronized修飾子を付ける
    public class HasSelfPrivateNum {
        private int num = 0;
    
        synchronized public void addI(String username) {
            try {
                //int num = 0;
                if (username.equals("a")) {
                    num = 100;
                    System.out.println("a set over!");
                    Thread.sleep(2000);
                } else {
                    num = 200;
                    System.out.println("b set over!");
                }
                System.out.println(String.format("%s num=%s", username, num));
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }
    

    1.3複数のオブジェクトの複数のロック


    2つのスレッドは、同じクラスの2つの異なるインスタンスの同じ名前の同期方法にそれぞれアクセスします.効果は非同期で実行されます.
    synchronized取得Yesロックはオブジェクトロックです.コードやメソッドをロックとして使用するのではなく、したがって、2つのインスタンスオブジェクトJVMは2つのロックを生成します.

    1.4 synchronizedメソッドとロックオブジェクト


    synchronized宣言を呼び出す方法は、必ずキューに並んで実行されます.また、同期化は共有リソースへの読み書きアクセスのみが必要です.共有リソースでない場合は、同期化する必要はありません.
  • コード検証synchronizedキューアクセス
  • public class MyObject {
        synchronized public void methodA() {
            try {
                System.out.println(String.format("begin method threadname:%s", Thread.currentThread().getName()));
                Thread.sleep(2000);
                System.out.println("end!");
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }
    
    public class ThreadAA extends Thread {
        private MyObject obj;
    
        public ThreadAA(MyObject obj) {
            super();
            this.obj = obj;
        }
    
        @Override
        public void run() {
            super.run();
            obj.methodA();
        }
    }
    
    
    public class ThreadBB extends Thread {
        private MyObject obj;
    
        public ThreadBB(MyObject obj) {
            super();
            this.obj = obj;
        }
    
        @Override
        public void run() {
            super.run();
            obj.methodA();
        }
    }
    
    
  • clientテスト
  • MyObject myObject = new MyObject();
    ThreadAA a = new ThreadAA(myObject);
    a.setName("a");
    ThreadBB b = new ThreadBB(myObject);
    b.setName("b");
    a.start();
    b.start();
    
  • 出力結果
  • begin method threadname:a
    end!
    begin method threadname:b
    end!
    

    ただしMyObjectに対しては別の方法が存在する.synchronizedメソッドでない場合は、synchronized同期呼び出しを待つのではなく、非同期呼び出しです.

    1.5 synchronizedロック再入可能


    synchronizedメソッド/ブロックの内部で本クラスの他のsynchronizedメソッド/ブロックを呼び出すと、ロックは永遠に取得できます.
    親子継承環境もサポートされています.サブクラスでsynchronizedメソッドを呼び出します.サブクラスのメソッドも親クラスのsynchronizedメソッドを呼び出します.この時はロックを取得できます.

    1.6異常が発生し、ロックが自動的に解除される


    スレッドがコードを実行すると、例外が発生します.持っているロックは自動的に解放されます.

    1.7synchronizedキーワードは継承されていません


    親メソッドでsynchronizedを定義します.子クラスが親クラスを書き換える方法.ではsynchronizedは継承できません.サブクラスに個別に追加する必要があります.

    2.synchronized同期文ブロック


    キーワードsynchronized宣言メソッドは、場合によっては弊害があります.例えば、Aスレッドは同期メソッドを呼び出して長時間のタスクを実行する.では、Bスレッドは待たなければなりません.したがってsynchronizedでコードブロックを同期することができます.
     synchronized (this) {}
    

    もちろん、thisをロックせずに、this以外のオブジェクトをロックすることもできます.もちろんStringタイプではありません
    private Object lock=new Object();
    synchronized (lock){}
    

    注意:lockオブジェクトがメソッド内の変数である場合、同期の効果は得られません.このロックされたクラスに必要なクラスの変数.
    複数のスレッドは、同じオブジェクト内の異なるsynchronizedメソッドを呼び出します.呼び出しの効果も同期して実行されます.ふさがっている.

    2.1 synchronized同期方法の無限待ちを解決する


    1つのクラスにsynchronizedで記述された方法が複数ある場合.複数のスレッドがこのクラスにアクセスする異なる方法.スレッドAメソッドのクラスAメソッドの場合.スレッドBがクラスにアクセスするBメソッド.メソッドAにデッドサイクルが発生すると、スレッドBは無限に待機する.ソリューション:synchronizedコードブロック方式を使用します.

    2.2プログラム設計時に設計双方が各自のロックを持つことを避ける


    プログラム設計の場合、双方が相手のロックを持っている場合を避け、互いに待機し、デッドロックが発生する場合があります.

    2.3複数のスレッドが同じロックを持っているかどうかに注意する


    複数のスレッドが同じインスタンス化オブジェクトを持っている場合、この処理方法は同期され、異なるインスタンス化オブジェクトであれば非同期です.

    2.4volatile


    変数を複数のスレッド間で表示します.共通スタックに変数を配置します.
    volatile private  int count=0;
    

    2.5 volatileとsynchronizedの比較


    volatile
  • 1.volatileはスレッド同期の軽量実装
  • である.
  • 2.volatileは変数
  • のみを修飾できます
  • 3.マルチスレッドアクセスvolatileで渋滞は発生しません
  • 4.volatileはデータの可視性を保証できるが、データの原子性は保証されない
  • synchronized
  • 1.synchronizedは、方法、コードブロック
  • を修飾することができる
  • 2.マルチスレッドアクセスsynchronizedでブロック
  • が発生します.
  • 3.synchronizedは原子性
  • を保証できる

    2.6atomic


    package java.util.concurrent.atomicパッケージのクラスはすべて原子性のクラスで、スレッドは安全です
    注意:atomicの中のクラスは修飾子ではありません.パラメータタイプ
    AtomicInteger count = 0;