JAvaにおける単例モードの実現
実装の一例
単一のスキーマは、クラスにインスタンスが1つしかないことを確認し、グローバルで一意のアクセスポイントを提供することです.唯一のアクセスポイントは、構築メソッドをプライベートにする必要があります.インスタンスを取得するための共通メソッドは、グローバルにインスタンスです.インスタンスのオブジェクトはstaticでなければなりません.
以上の点に基づいて、基本的な怠け者式と餓漢式を書くことができます.
一、餓漢式
二、怠け者の遅延使用時にオブジェクトを作成する
三、怠け者式マルチスレッドでの最適化->二重検証式
①上記の怠け者式はスレッドが安全でない以上、ロックをかけてスレッド安全にする
加同期頼漢限界:マルチスレッドの下のある時点で1つのスレッドしかアクセスできず、他のスレッドがブロックされるのを待つ時間が長すぎて、他のスレッドのsingleがインスタンス化されていても、効率が心配されています.
②さらに最適化すると、上にスレッドやsingleインスタンスを取得したオブジェクトがある以上、if判定に入る必要はないので、同期ロックの範囲をメソッド全体からifに絞ることができる
しかし、この時また新しい問題が発生しました.両方のスレッドT 1,T 2がsingleが得られなかった例であると仮定する.1.T 1が先に同期ブロックに入った後、T 2がifに入って同期ブロックの前で待機した.2.T 1インスタンス化を完了し、同期ブロックを終了して同期ロックを解放し、returnは3.現在、T 2がifに古く入っているため、singleがインスタンス化されていることを知らないため、T 2も同期ブロックに入ってもう一度インスタンス化し、戻る.これで2回getSingleを作ったのは統一オブジェクトではありません!その根本的な原因はT 2が同期ブロックに入る中でsingleがすでに実例したかどうかを一度に判断したことがないからである.
③上記解析T 2が同期ブロックに入った以上、同期を最後に終了した他のスレッドがインスタンスを作成したとは判断していない.それに判断が加わる
④命令再配列がある以上、命令再配列はさせず、jdkはvolatileキーワードを提供する.究極版は以下の二重チェックロック式
四、静的内部クラス
プライベートな静的内部クラスを使用して外部アクセスを防止し、静的な外部クラスオブジェクトはすべて1つの内部クラスしかないため、内部クラスはオブジェクトを作成するために使用され、単一の例に合致するグローバルには1つのオブジェクトしかありません.さらに、グローバルなアクセスポイントを提供する共通のメソッド取得インスタンスの一例を提供します.
利点:外部クラスがロードされると、内部クラスはロードされず、内部がロードされないとnewされず、内部クラスのみが参照されます.すなわちgetInstanceが呼び出されると、JVMが内部クラスをロードするメリットが得られます.スレッドセキュリティ、遅延ロードのインスタンス化
この5つのケースはクラスのアクティブリファレンスと呼ばれ、
仮想機会は、1つのクラスの()メソッドがマルチスレッド環境で正しくロックされ、同期されることを保証します.複数のスレッドが同時に1つのクラスを初期化すると、1つのスレッドだけがこのクラスの()メソッドを実行し、他のスレッドはアクティブなスレッド実行()メソッドが完了するまで待機をブロックする必要があります.
単一のスキーマは、クラスにインスタンスが1つしかないことを確認し、グローバルで一意のアクセスポイントを提供することです.唯一のアクセスポイントは、構築メソッドをプライベートにする必要があります.インスタンスを取得するための共通メソッドは、グローバルにインスタンスです.インスタンスのオブジェクトはstaticでなければなりません.
以上の点に基づいて、基本的な怠け者式と餓漢式を書くことができます.
一、餓漢式
public class FirstSingle {
//
private FirstSingle() {
}
private static FirstSingle single = new FirstSingle();
public static FirstSingle getSingle() {
return single;
}
}
二、怠け者の遅延使用時にオブジェクトを作成する
public class FirstSingle {
//
private FirstSingle() {
}
private static FirstSingle single = null;
public static FirstSingle getSingle() {
// , , 。 , , , !
if(single == null)
single = new FirstSingle();
return single;
// : if
}
}
三、怠け者式マルチスレッドでの最適化->二重検証式
①上記の怠け者式はスレッドが安全でない以上、ロックをかけてスレッド安全にする
public class FirstSingle {
// ,
private FirstSingle() {
}
private static FirstSingle single = null;
public synchronized static FirstSingle getSingle() {
if(single == null)
single = new FirstSingle();
return single;
// : , , single ,
}
}
加同期頼漢限界:マルチスレッドの下のある時点で1つのスレッドしかアクセスできず、他のスレッドがブロックされるのを待つ時間が長すぎて、他のスレッドのsingleがインスタンス化されていても、効率が心配されています.
②さらに最適化すると、上にスレッドやsingleインスタンスを取得したオブジェクトがある以上、if判定に入る必要はないので、同期ロックの範囲をメソッド全体からifに絞ることができる
public class FirstSingle {
// , single , if , if
private FirstSingle() {
}
private static FirstSingle single = null;
public static FirstSingle getSingle() {
if(single == null)
synchronized(FirstSingle.class) {
// 。 T1、T2 single 。
//1.T1 ,T2 if 。
//2.T1 , ,return
//3. T2 if , single , T2 , 。 getSingle !
// T2 single !
single = new FirstSingle();
}
return single;
}
}
しかし、この時また新しい問題が発生しました.両方のスレッドT 1,T 2がsingleが得られなかった例であると仮定する.1.T 1が先に同期ブロックに入った後、T 2がifに入って同期ブロックの前で待機した.2.T 1インスタンス化を完了し、同期ブロックを終了して同期ロックを解放し、returnは3.現在、T 2がifに古く入っているため、singleがインスタンス化されていることを知らないため、T 2も同期ブロックに入ってもう一度インスタンス化し、戻る.これで2回getSingleを作ったのは統一オブジェクトではありません!その根本的な原因はT 2が同期ブロックに入る中でsingleがすでに実例したかどうかを一度に判断したことがないからである.
③上記解析T 2が同期ブロックに入った以上、同期を最後に終了した他のスレッドがインスタンスを作成したとは判断していない.それに判断が加わる
public class FirstSingle {
// T2 , 。
private FirstSingle() {
}
private static FirstSingle single = null;
public static FirstSingle getSingle() {
if(single == null)
synchronized(FirstSingle.class) {
if(single == null)
// if ,
// JVM , , 。
//single = new FirstSingle(); :
//1. new FirstSingle() , 0
//2.
//3.single
// ,1—>3—>2, T1 1,3 , ,T1 2, return;T2 single null, single , 2 、 , 。
// T1 T2 2 , T2 。
single = new FirstSingle();
}
return single;
}
}
④命令再配列がある以上、命令再配列はさせず、jdkはvolatileキーワードを提供する.究極版は以下の二重チェックロック式
public class FirstSingle {
// , ,jdk volatile 。
private FirstSingle() {
}
// volatile singlon , 、 jvm
private static volatile FirstSingle single = null;
public static FirstSingle getSingle() {
//
if(single == null)
synchronized(FirstSingle.class) {
// if , ,
if(single == null)
//new volatile jvm , single , volatile
single = new FirstSingle();
}
return single;
}
}
四、静的内部クラス
プライベートな静的内部クラスを使用して外部アクセスを防止し、静的な外部クラスオブジェクトはすべて1つの内部クラスしかないため、内部クラスはオブジェクトを作成するために使用され、単一の例に合致するグローバルには1つのオブジェクトしかありません.さらに、グローバルなアクセスポイントを提供する共通のメソッド取得インスタンスの一例を提供します.
利点:外部クラスがロードされると、内部クラスはロードされず、内部がロードされないとnewされず、内部クラスのみが参照されます.すなわちgetInstanceが呼び出されると、JVMが内部クラスをロードするメリットが得られます.スレッドセキュリティ、遅延ロードのインスタンス化
package designpatten.singleton;
//
public class SingletonInnerClass {
private SingletonInnerClass() {}
// ,
private static class SingletonHolder {
static SingletonInnerClass single = new SingletonInnerClass();
}
public SingletonInnerClass getInstance() {
return SingletonHolder.single;
}
}
// : , , getInstance single
// , getInstance ,
// :jvm static , ,
// jvm 5 , .
//jvm , 。 getInstance
:JAVA 5 。
1. new、getstatic、setstatic invokestatic 4 , java :new 、 (final 、 )、 。
2. java.lang.reflect , , 。
3. , , 。
4. , ( main() ), 。
5. JDK 1.7 , java.lang.invoke.MethodHandle REF_getStatic、REF_putStatic、REF_invokeStatic , , 。
この5つのケースはクラスのアクティブリファレンスと呼ばれ、
仮想機会は、1つのクラスの()メソッドがマルチスレッド環境で正しくロックされ、同期されることを保証します.複数のスレッドが同時に1つのクラスを初期化すると、1つのスレッドだけがこのクラスの()メソッドを実行し、他のスレッドはアクティブなスレッド実行()メソッドが完了するまで待機をブロックする必要があります.