まず、非スレッド セーフな初期化シングルトン モードを見てみましょう
public class UnsafeLazyInitialization { private static UnsafeLazyInitialization instance; public static UnsafeLazyInitialization getInstance(){ if(instance == null){ //1: 线程A执行 instance = new UnsafeLazyInitialization(); //2: 线程B执行 } return instance; } }
UnsafeLazyInitialization クラスで、スレッド A がコード 1 を実行すると仮定します。 、スレッド B コード 2 が実行されると、スレッド A はインスタンス参照オブジェクトがまだ初期化されていないことを認識する可能性があります。
UnsafeLazyInitialization クラスでは、getInstance() メソッドを同期して、スレッドセーフな遅延初期化を実現できます。サンプル コードは次のとおりです:
public static synchronized UnsafeLazyInitialization getInstance(){ if(instance == null){ //1: 线程A执行 instance = new UnsafeLazyInitialization(); //2: 线程B执行 } return instance; } }
上記のコードは getInstance() を実行しているためです。メソッド 同期処理を行わないと、同期プログラムのオーバーヘッドが増加する可能性があります。 getInstance()が複数のスレッドから頻繁に呼び出される場合にはプログラムの実行性能が低下し、逆に複数のスレッドから呼び出されない場合にはgetInstance()メソッドの遅延初期化メソッドが性能に影響を与えます。
JVM 1.6 より前では、synchronized は重いロックであり、パフォーマンスを非常に消費するため、パフォーマンスを向上させるために二重チェック ロック (ダブルチェック ロック) ソリューションが考えられました。サンプル コードは次のとおりです。 :
public class DoubleCheckedLocking { //1、 private static Instance instance; //2、 public static Instance getInstance(){ //3、 if(instance == null){ //4、第一次检查 synchronized (DoubleCheckedLocking.class){ //5、枷锁 if(instance == null){ //6、第二次检查 instance = new Instance(); //7、问题的根源在这里 } //8、 } } return instance; } }
上記のコードに示すように: ステップ 4 の最初のチェック インスタンスが null でない場合、次のロック操作を実行する必要はありません。これにより、同期ロックによって引き起こされるパフォーマンスの問題が大幅に軽減されます。上記のコードでは問題ないようです。 1. 複数のスレッド ビューが新しいオブジェクトを作成する場合、synchronized キーワードを使用すると、1 つのスレッドだけがオブジェクトを正常に作成するようにできます。
2. インスタンス オブジェクトが作成されている場合は、getInstatnce() メソッドを通じてオブジェクト インスタンスを直接取得します。
上記のコードは完璧に見えますが、ステップ 4 が実行されると、インスタンス! =null、インスタンスの参照オブジェクトはまだ初期化されていない可能性があります。
上記のコードをステップ 7 (instance = new Instance();) まで実行すると、オブジェクトが作成されます。次の 3 つのステップ :
memory = allocate() //1.分配内存空间memory ctorInstance(memory) //2, 初始化对象在内存 分配内存空间memory上初始化 Singleton 对象 instance = memory //3、设置 instance 指向刚分配的内存地址memory
上記のコード 2 と 3 の 3 行は並べ替えられる可能性があります (JTI コンパイラでは、この並べ替えは実際に行われます) ステップ 2 と 3 の並べ替え後の実行シーケンス
memory = allocate() //1.分配内存空间memory instance = memory //3、设置 instance 指向刚分配的内存地址memory // 注意此时instance对象还没有被初始化,但是instance的引用已经不是null了。 ctorInstance(memory) //2, 初始化对象在内存 分配内存空间memory上初始化 Singleton 对象
マルチスレッドの実行シーケンスを見てみましょう
#上記のコードの 7 行目 instance = new Instance( ); スレッド A の場合、命令の並べ替え (2、3) が発生すると、別のスレッド B は、コードの 4 行目のインスタンスが空ではないと判断する可能性があります。次にスレッド B はインスタンスの参照オブジェクトにアクセスしますが、インスタンス オブジェクトは A によって初期化されていない可能性があります。このとき、スレッド B が初期化されていないオブジェクトにアクセスし、null ポインタ エラーが発生する可能性があります。 問題解決策1. 命令 2 と 3 を再配置することはできません。 2. 2 と 3 の並べ替えを許可しますが、他のスレッドが並べ替えを参照できないようにしますvolatile ベースのソリューション上記のコードに基づくと、volatile キーワードを追加するだけで済みます。インスタンスステートメントへの追加です。次のコードです。public class DoubleCheckedLocking { //1、 private static volatile Instance instance; //2、 public static Instance getInstance(){ //3、 if(instance == null){ //4、第一次检查 synchronized (DoubleCheckedLocking.class){ //5、枷锁 if(instance == null){ //6、第二次检查 instance = new Instance(); //7、问题的根源在这里 } //8、 } } return instance; } }
以上がJavaでの二重チェックロックの問題を解決する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。