二重検査ロック(double-checked locking)と単例モードを深く理解する

6769 ワード

ロード、起動プログラムのファイルまたは情報のロードを表す
JAvaのクラスのバイトコードファイルがメモリにロードされ、コンテンツがioストリームとしてメモリに格納されます.
User user=new User(「男」,26);この文はいくつかのことをしました.
  • newはUserを使ったからだ.class、だからUserを見つけた.classファイルをメモリにロードする
  • このクラスのstaticコードブロックを実行し、もしあれば、Userに与える.classクラスの初期化
  • スタックメモリにスペースを開き、メモリアドレスを割り当てます.
  • は、スタックメモリにオブジェクト固有のプロパティを設定し、デフォルトの初期化を行います.
  • 属性を明示的に初期化する
  • オブジェクトを構築するブロック初期化
  • .
  • オブジェクトに対応するコンストラクタの初期化
  • メモリアドレスをスタックメモリに付与p変数は特に注意され、ステップ7とステップ8で、前後の発生順序はランダムな
  • である.
    だらだらロード
    餓漢式単例
    public class Singleton        
    {        
        private static Singleton instance = new Singleton();        
        private Singleton(){        
            …        
        }        
        public static Singleton getInstance(){        
                 return instance;         
        }        
    } 

    このようなコードの欠点は、クラスを最初にロードするときにSingletonインスタンスを作成することです.このような結果は、インスタンスを作成するときにこのインスタンスが必要ではない可能性があるため、私たちが望んでいるのとは異なります.同時に、このSingletonインスタンスの作成がシステムリソースを非常に消費し、アプリケーションが常にSingletonインスタンスを使用していない場合、Singletonを作成するために消費されるシステムリソースは無駄になります.
    このような状況を回避するために,通常,不活性ロードのメカニズム,すなわち,使用時に作成される.以上のコードの不活性ロードコードは以下の通りである:怠け者式単例
    public class Singleton{        
        private static Singleton instance = null;        
        private Singleton(){        
            …        
        }        
        public static Singleton getInstance(){        
            if (instance == null)        
                instance = new Singleton();         
                    return instance;         
        }        
    } 

    スレッドセキュリティの問題
    これは、2つのスレッドAとBが同時にこの方法を実行した場合、以下のように実行される.
  • Aはif判定に入るが、このときinstanceはnullであるためif内
  • に入る.
  • Bはif判定に入るが、このときAはまだinstanceを作成していないためinstanceもnullであるため、Bもif内
  • に入る.
  • Aはinstanceを作成し、
  • を返します.
  • Bもinstanceを作成し、
  • を返します.
    この時点で問題が発生し、私たちの単一の例は2回作成されましたが、これは私たちが望んでいるものではありません.
    3さまざまな解決策とその存在する問題3.1 Classロックメカニズムを使用する以上の問題の最も直感的な解決策は、getInstanceメソッドにsynchronize接頭辞を付けることであり、getInstanceメソッドを呼び出すたびに1つの既存の呼び出ししか許可されない.
    public static synchronized Singleton getInstance(){        
        if (instance == null)        
        instance = new Singleton();         
        return instance;         
    }  

    この解決策は確かにエラーの発生を防止することができるが、getInstanceメソッドを呼び出すたびにSingletonのロックを取得しなければならないが、実際には、単一のインスタンスが作成された後、その後の要求は反発メカニズムを使用する必要はない.
    3.2 double-checked locking上記の問題を解決するためにdouble-checked lockingの解決策を提案したことがある
    public static Singleton getInstance(){        
        if (instance == null)        
            synchronized(instance){        
                if(instance == null)        
                    instance = new Singleton();        
            }        
        return instance;         
    } 

    このコードがどのように動作しているかを見てみましょう.まず、スレッドがリクエストを発行すると、instanceがnullであるかどうかを確認し、そうでない場合はその内容を直接返し、synchronizedブロックに入るのにかかるリソースを回避します.次に、第2節で述べた場合でも、2つのスレッドが同時に第1のif判定に入った場合、synchronizedブロックのコードを順番に実行する必要があり、第1のコードブロックに入ったスレッドは新しいSingletonインスタンスを作成し、後続のスレッドはif判定に合格できないため、余分なインスタンスを作成しない.
    上記の説明は、私たちが直面しているすべての問題を解決したようですが、実際には、JVMの観点から、これらのコードにエラーが発生する可能性があります.JVMでは、Javaコマンドが実行されます.Java命令におけるオブジェクトの作成と付与操作は別々に行われる,すなわちinstance=new Singleton()である.文は2つのステップに分けて実行されます.しかし、JVMは、この2つの操作の前後順序を保証するものではありません.つまり、JVMが新しいSingletonインスタンスに空間を割り当て、instanceメンバーに直接値を割り当ててから、このSingletonインスタンスを初期化する可能性があります.(すなわち、メモリアドレスを指定してから初期化する)エラーを可能にし、A、Bの2つのスレッドを例に挙げます.
  • A,Bスレッドは同時に第1 if判定
  • に入る.
  • Aはまずsynchronizedブロックに入り、instanceはnullであるため、instance=new Singleton()を実行する.
  • JVM内部の最適化メカニズムのため、JVMはまずSingletonインスタンスに割り当てられた空きメモリをいくつか描き、instanceメンバーに値を割り当て(JVMがこのインスタンスの初期化を開始していないことに注意)、その後Aはsynchronizedブロックを離れた.
  • Bはsynchronizedブロックに入り、instanceはnullではないため、synchronizedブロックをすぐに離れ、その結果をメソッドを呼び出すプログラムに返す.
  • このとき、BスレッドはSingletonインスタンスを使用しようとしたが、初期化されていないことに気づき、エラーが発生した.

  • 4内部クラスによってマルチスレッド環境における単一のモードを実現遅いロードを実現するために、getInstanceを呼び出すたびに反発して実行する必要がなく、最も便利な解決策は以下の通りである.
    public class Singleton{        
        private Singleton(){        
            …        
        }        
        private static class SingletonContainer{        
            private static Singleton instance = new Singleton();        
        }        
        public static Singleton getInstance(){        
            return SingletonContainer.instance;        
        }        
    } 

    JVM内部のメカニズムは、クラスがロードされると、このクラスのロードプロセスがスレッド反発することを保証することができる.これにより、getInstanceを初めて呼び出すと、JVMはinstanceが一度だけ作成されることを保証し、instanceに割り当てられたメモリの初期化が完了することを保証し、3.2の問題を心配する必要はありません.さらに、この方法は、最初の呼び出し時に反発メカニズムのみを使用し、3.1の非効率的な問題を解決する.最後にinstanceはSingletonContainerクラスを最初にロードするときに作成され、SingletonContainerクラスはgetInstanceメソッドを呼び出すときにロードされるため、不活性ロードも実現される.