JAva 23種類の設計モード——二、単例モード
7112 ワード
ソースコードは私のgithubとgiteeで取得します
紹介する
Singleton Pattern(Singleton Pattern)はJavaの中で最も簡単な設計モードの一つである.このタイプの設計モードは、オブジェクトを作成するための最適な方法を提供する作成モードに属します.このモードは、独自のオブジェクトを作成し、単一のオブジェクトのみが作成されることを保証する単一のクラスに関連します.このクラスは、クラスのオブジェクトをインスタンス化する必要がなく、直接アクセスできる独自のオブジェクトにアクセスする方法を提供します.注意:1、単一のクラスには1つのインスタンスしかありません. 2、単一のインスタンスクラスは、独自の一意のインスタンスを独自に作成する必要があります. 3、単一のクラスは、このインスタンスを他のすべてのオブジェクトに提供する必要があります.
インプリメンテーションモード
餓漢式単例(静的定数、スレッド安全)
名前の通り、餓漢式の単例は「空腹」なので、最初から唯一の単例インスタンスを作成しましたが、このインスタンスを使用したことがないとメモリの無駄になります.
出力結果
出力が同じインスタンスであることがわかります
餓漢式単例(静的コードブロック、スレッドセキュリティ)
従来の方法と同様に、クラスインスタンス化のプロセスを静的コードブロック、すなわちクラスマウント時に置くだけで、
静的コードブロック内のコードを実行する場合、メリットとデメリットは以前と同じです.
しゅつりょく
怠け者式(スレッドが安全ではない)
同様に、その名の通り、怠け者式の単例は怠け者だ.インスタンスは、使用するときにのみ作成されます.
しゅつりょく
ここでは極端な状況を選びました.もしあなたのパソコンの構成がよければ、何度も実行した結果、単一のモードに合っている可能性があります.
怠け者式(同期方法、スレッドセキュリティ)
上記の方法でスレッドが安全でない場合があるのは、マルチスレッドの場合、単一の例が作成されたかどうかを同時に判断する複数のスレッドがある可能性があるからです.ではこの問題を解決するにはgetInstance()メソッドを同期するだけです
結果
しかし、synchronizedは非常に重い同期ロックであり、getInstance()を実行するたびに同期し、効率に極めて影響します.
怠け者式(ダブルチェック、スレッドセキュリティ)
二重検査錠は、二重検査錠とも呼ばれ、怠け者式と餓漢式の両者の長所と短所を統合したものである.上記のコード実装を見ると、synchronizedキーワードの内外にif条件判断が施されているのが特徴で、スレッドの安全を保証するとともに、直接ロックよりも実行効率が向上し、メモリスペースも節約できます.
実行結果
推奨
静的内部クラス(スレッドセキュリティ)
結果
この方式は比較的簡単で,使用を推奨する
列挙(スレッドセキュリティ)
結果
列挙は単例モードを実現し,最も簡潔で推奨されていることがわかる.しかし、それが簡潔であるため、可読性が悪い.
紹介する
Singleton Pattern(Singleton Pattern)はJavaの中で最も簡単な設計モードの一つである.このタイプの設計モードは、オブジェクトを作成するための最適な方法を提供する作成モードに属します.このモードは、独自のオブジェクトを作成し、単一のオブジェクトのみが作成されることを保証する単一のクラスに関連します.このクラスは、クラスのオブジェクトをインスタンス化する必要がなく、直接アクセスできる独自のオブジェクトにアクセスする方法を提供します.注意:
インプリメンテーションモード
餓漢式単例(静的定数、スレッド安全)
名前の通り、餓漢式の単例は「空腹」なので、最初から唯一の単例インスタンスを作成しましたが、このインスタンスを使用したことがないとメモリの無駄になります.
/**
*
* : , , ,
* : , ,
*/
public class SingletonTest01 {
public static void main(String[] args) {
Signleton instance1= Signleton.getInstance();
Signleton instance2 = Signleton.getInstance();
System.out.println(instance1==instance2);
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
}
}
class Signleton{
//1、 , new
private Signleton(){ }
//2、
private final static Signleton instance = new Signleton();
//3、 ,
public final static Signleton getInstance(){
return instance;
}
}
出力結果
true
1163157884
1163157884
出力が同じインスタンスであることがわかります
餓漢式単例(静的コードブロック、スレッドセキュリティ)
従来の方法と同様に、クラスインスタンス化のプロセスを静的コードブロック、すなわちクラスマウント時に置くだけで、
静的コードブロック内のコードを実行する場合、メリットとデメリットは以前と同じです.
/**
* , , ,
* ,
*/
public class SingletonTest02 extends Thread{
public static void main(String[] args) {
Signleton instance1= Signleton.getInstance();
Signleton instance2 = Signleton.getInstance();
System.out.println(instance1==instance2);
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
}
}
class Signleton{
//1、 , new
private Signleton(){}
//2、
private static Signleton instance;
static {// ,
instance = new Signleton();
}
//3、 ,
public final static Signleton getInstance(){
return instance;
}
}
しゅつりょく
true
1163157884
1163157884
怠け者式(スレッドが安全ではない)
同様に、その名の通り、怠け者式の単例は怠け者だ.インスタンスは、使用するときにのみ作成されます.
/**
* -
* : ,
* , if ,
* , , ( ),
* ,
*/
public class SingletonTest03 {
public static void main(String[] args) {
for (int i = 0; i <10 ; i++) {
new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();
}
}
}
class Signleton{
private static Signleton instance;
private Signleton(){}
// , , instance
public static Signleton getInstance(){
if(instance == null){//
instance = new Signleton();
}
return instance;
}
}
しゅつりょく
546405844
135417039
135417039
802181073
135417039
135417039
135417039
802181073
135417039
135417039
ここでは極端な状況を選びました.もしあなたのパソコンの構成がよければ、何度も実行した結果、単一のモードに合っている可能性があります.
怠け者式(同期方法、スレッドセキュリティ)
上記の方法でスレッドが安全でない場合があるのは、マルチスレッドの場合、単一の例が作成されたかどうかを同時に判断する複数のスレッドがある可能性があるからです.ではこの問題を解決するにはgetInstance()メソッドを同期するだけです
/**
*
* , getInstance()
*/
public class SingletonTest04 {
public static void main(String[] args) {
for (int i = 0; i <10 ; i++) {
new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();
}
}
}
class Signleton{
private static Signleton instance;
private Signleton(){}
// , , instance
public static synchronized Signleton getInstance(){
if(instance == null){//
instance = new Signleton();
}
return instance;
}
}
結果
802181073
802181073
802181073
802181073
802181073
802181073
802181073
802181073
802181073
802181073
しかし、synchronizedは非常に重い同期ロックであり、getInstance()を実行するたびに同期し、効率に極めて影響します.
怠け者式(ダブルチェック、スレッドセキュリティ)
二重検査錠は、二重検査錠とも呼ばれ、怠け者式と餓漢式の両者の長所と短所を統合したものである.上記のコード実装を見ると、synchronizedキーワードの内外にif条件判断が施されているのが特徴で、スレッドの安全を保証するとともに、直接ロックよりも実行効率が向上し、メモリスペースも節約できます.
/**
* -
* if ,
* ,
*/
public class SingletonTest05 {
public static void main(String[] args) {
for (int i = 0; i <10 ; i++) {
new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();
}
}
}
class Signleton{
private static volatile Signleton instance;//volatile
private Signleton(){}
// , , ,
public static Signleton getInstance() {
if (instance == null) {
synchronized (Signleton.class) {
if (instance == null) {
instance = new Signleton();
}
}
}
return instance;
}
}
実行結果
79372097
79372097
79372097
79372097
79372097
79372097
79372097
79372097
79372097
79372097
推奨
静的内部クラス(スレッドセキュリティ)
/**
*
*
* Signleton , , SignletonInstance
*
* , ,
*/
public class SingletonTest07 {
public static void main(String[] args) {
for (int i = 0; i <10 ; i++) {
new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();
}
}
}
class Signleton{
//
private Signleton(){}
// , Signleton
private static class SignletonInstance{
private static final Signleton instance = new Signleton();
}
// , SignletonInstance.instance
public static Signleton getInstance() {
return SignletonInstance.instance;
}
}
結果
79372097
79372097
79372097
79372097
79372097
79372097
79372097
79372097
79372097
79372097
この方式は比較的簡単で,使用を推奨する
列挙(スレッドセキュリティ)
/**
* @author codermy
* @createTime 2020/5/14
*
* jdk1.5 ,
* ,
*/
public class SingletonTest08 {
public static void main(String[] args) {
Singleton singleton = Singleton.INSTANCE;
singleton.Ok();
for (int i = 0; i <10 ; i++) {
new Thread(() -> System.out.println(Singleton.INSTANCE.hashCode()) ).start();
}
}
}
enum Singleton{
INSTANCE;//
public void Ok(){
System.out.println("ok");
}
}
結果
ok
858497792
858497792
858497792
858497792
858497792
858497792
858497792
858497792
858497792
858497792
列挙は単例モードを実現し,最も簡潔で推奨されていることがわかる.しかし、それが簡潔であるため、可読性が悪い.