ホームページ  >  記事  >  Java  >  Java シングルトン パターンでのスレッド セーフティの問題を解決するにはどうすればよいですか?

Java シングルトン パターンでのスレッド セーフティの問題を解決するにはどうすればよいですか?

王林
王林転載
2023-05-09 19:28:061426ブラウズ

    1. マルチスレッドを使用するときに考慮すべき要素

    効率の向上:マルチスレッドを使用すると、 CPU リソースを最大限に活用し、タスクの効率を向上させます。
    スレッド セーフティ:マルチスレッドの使用に関する最も基本的なことは、スレッド セーフティを確保することです。

    したがって、マルチ スレッドを設計するときは、スレッド化されたコードでは、スレッド セーフティを満たすことを前提として、タスクの実行効率を可能な限り向上させる必要があります。 ##スレッド セーフティを考慮する:

    コードにはセキュリティ上の問題はありません。共有変数は操作しません共有変数を読み取るには、volatile を使用して変数を変更します共有変数への書き込みは、同期ロックを使用します

    2. シングルトン モード

    シングルトン モードでは、複数のインスタンスを作成するのではなく、プログラム内に特定のクラスのインスタンスが 1 つだけ存在することを保証できます。
    例: DataSource (データ接続プール)、データベースに必要な接続プール オブジェクトは 1 つだけです

    シングルトン モードはハングリー モードとレイジー モードに分けられます


    1. ハングリー モード

    ハングリー モードはクラスのロード時にインスタンスを作成します

    このメソッドはスレッドセーフです (JVM は内部でロックを使用します。つまり、複数のスレッドが静的メソッドを呼び出します。1 つのスレッドだけがロックを競合して作成を完了し、1 回だけ実行されます)

    実装コード:

    public class Singleton {
        private static Singleton instance = new Singleton();
        private Singleton(){
     
        }
        public static Singleton getInstance(){
            return instance;
        }
    }

    2. 遅延モード

    遅延モードでは、クラスのロード時にインスタンスが作成されず、初めて使用されるときにのみインスタンスが作成されます。

    実装コード:
    public class Singleton {
        private static Singleton instance = null;
        private Singleton(){
     
        }
        public static Singleton getInstance(){
            if(instance == null){
                instance = new Singleton();
            }
            return instance;
        }
    }

    シングルスレッドで上記のコードを観察してください。スレッドの安全性の問題はありませんが、マルチスレッド環境ではセキュリティの問題がありますか?

    分析:

    インスタンスが作成されていないときに、複数のスレッドが getInstance メソッドを呼び出すと、複数のインスタンスが作成される可能性があり、スレッド セーフになります。問題
    ただし、インスタンスが作成されると、後続のスレッドが getInstance メソッドを呼び出したときにスレッド セーフティの問題は発生しません

    結果:
    インスタンスが最初に作成されるときにスレッド セーフティの問題が発生します

    3. 遅延モード (同期を使用して改善) 同期変更を使用します ????‍??????️コードは次のとおりです:

    public class Singleton {
        private static Singleton instance = null;
        private Singleton(){
     
        }
        public static synchronized Singleton getInstance(){
            if(instance == null){
                instance = new Singleton();
            }
            return instance;
        }
    }

    この方法でスレッド セーフを実装する場合、どのような問題がありますか?

    分析:

    メソッドに対して同期変更を使用します。つまり、メソッドが呼び出されるたびにロックが競合しますが、インスタンスは作成する必要があるのは 1 回だけです。つまり、インスタンスを作成した後でも、このメソッドを呼び出すと、ロックを解放するためにロックを競合する必要があります。

    結果:これはスレッドセーフですが、効率は低いです。

    4. 遅延モード (二重チェック ロックを使用することで改善されます) 上記のコードに基づいて変更を加えます:

    Double を使用します。競合ロックの頻度を減らすための if 判定

    volatile を使用してインスタンスを変更する

    実装コード:

    public class Singleton {
        private static volatile Singleton instance = null;
        private Singleton(){
     
        }
        public static synchronized Singleton getInstance(){
            if(instance == null){ //外层的if判断:如果实例被创建直接return,不让线程再继续竞争锁
                //在没有创建实例时,多个线程已经进入if判断了
                //一个线程竞争到锁,其他线程阻塞等待
                synchronized (Singleton.class) {
                    //内层的if判断,目的是让竞争失败的锁如果再次竞争成功的话判断实例是否被创建,创建释放锁return,没有则创建
                    if(instance == null){ 
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

    double if の解析:

    外層の if 判定: インスタンスは一度だけ作成されます。インスタンスが作成されたら、その後の操作は必要ありません。直接戻ります。

    内層の if 判定:

    インスタンスが作成されていない場合、複数のスレッドが同時にロックを競合します。競合に成功してインスタンスを作成するのは 1 つのスレッドだけであり、競合に失敗した他のスレッドはブロックされて待機します。最初のスレッドが解放されるときロックが解除された場合、競合に失敗したスレッドは引き続き競合しますが、インスタンスはすでに作成されているため、もう一度 if を実行する必要があります。
    #3. volatile の原則

    volatile は可視性を保証します Java レベルでは、volatile はロックフリーの操作です。

    揮発性で変更された変数では、CPU はキャッシュ整合性プロトコルを使用して、最新のメイン メモリ データが確実に読み取られるようにします。

    ##キャッシュの一貫性: volatile によって変更された変数を別のスレッドが変更すると、CPU キャッシュ内の変数はリセットされます。この変数は無効です。この変数を操作するには、メイン メモリから再読み取る必要がありますJava シングルトン パターンでのスレッド セーフティの問題を解決するにはどうすればよいですか?

    4. Volatile 展開の問題 (理解)

    volatile が順序を保証しない場合 ダブルチェックロックの記述方法に問題はありますか?

    新しいオブジェクトについては、次の 3 つの命令に順番に分かれています。

    (1) オブジェクトのメモリ空間を確保します。

    (2) オブジェクトのインスタンスを作成します。 object

    ( 3) 変数に値を割り当てます

    通常の実行順序は (1)(2)(3) です。JVM は最適化して順序を (1)( 3)(2)

    この並べ替えの結果、メモリ空間が割り当てられた後、オブジェクトがインスタンス化される前に割り当てが完了する可能性があります。
    この間違った割り当ての後は、instance==null が確立されず、スレッドがインスタンス化されていないインスタンスの場合、そのプロパティとメソッドを使用するとエラーが発生します。

    volatile を使用して順序性を確保した後:

    スレッドは新しいオブジェクトに注意してください (1 )(2)(3) 順序は何ですか? 後続のスレッドによって取得されたインスタンスは、インスタンス化されたインスタンスです。
    CPU には、CPU レベルのロック メカニズム ベースがあります。揮発性変数の操作 ((1)(2)(3) すべての実行後、メイン メモリに書き戻してから、その変数に対して他のスレッドの操作を実行することを保証します)

    以上がJava シングルトン パターンでのスレッド セーフティの問題を解決するにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    声明:
    この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。