JAVAでの一例モード
6204 ワード
Javaでは、単一のモードの実現方法は以下のとおりです.
1.餓漢モード
餓漢モードは、最初から静的オブジェクトが作成され、その後もオブジェクトが存在します.このモードではスレッドセキュリティの問題はありません.
2.怠け者モード
餓漢モードでは、簡単な実現が利点です.しかし、instanceはSingleton 1がロードされるとstaticが存在する静的メソッド領域に作成されるという問題がある.データベース接続プールを実装するなど、この方法ではデータベースの接続リソースを作成し、実際のシステムでは使用しません.これは資源の浪費をもたらした.そのため、このような状況に対して、それに対応する怠け者パターンが現れた.すなわち、最初はオブジェクトを作成せず、必要に応じてnewします.
これはみんなが考えている最もよく使う怠け者のパターンの書き方です.しかし問題が来て、上記のモードはマルチスレッドの場合はスレッドが安全ではありません!すなわち,2つのスレッドがある場合,getInstance()と同時にinstanceの値がnullであると判断する.この場合、複数のインスタンスが作成されます.上記の問題を解決するために、ロックを導入しました.
上記の方法では、スレッドセキュリティの問題は確かに解決されましたが、より悪い問題をもたらしました.それは、リクエストごとにロックがかかっていることです.これにより、パフォーマンスに深刻な影響を及ぼします.より良い方法は、二重検査メカニズムを採用することです.
上記の単一の例は、クラスの初期化には時間がかかるという問題があり、同時に2つのスレッドがgetInstanceメソッドに同時に進入し、最初のスレッドがロックされた後、2番目のスレッドが空ではないと判断した場合、instalceを直接使用し、このとき最初のスレッドがSingletonDemo 3オブジェクトに対してまだインスタンス化されていない場合、そのオブジェクト内部に時間のかかる参照が存在する場合、データベース接続の場合、2番目のスレッドで使用されるオブジェクトが不完全になります.空のポインタが表示されます.だからもっと良い書き方はvolatileを加えることです.happen-beforeの原則を保証します.
このようにして、単一のモードが完全に解決されます.上記の方法は比較的に冗長で、もっと良い解決方法があるかどうか、幸いにも『effective java』という本を読んだことがあって、単例に対してもっと良い解決方法があります.
3.より良い解決方法
1つ目の方法は、静的内部クラスを使用することです.
上記の方法の実行結果:
SingletonHolderクラスは一度だけインスタンス化されていることがわかります.この方法は内部クラスを巧みに利用し,簡単なコードで単一例を実現し,スレッドセキュリティである.
方法2:『effective java』にはもっと簡単な書き方があります.それは列挙です.『effective java』の著者が最も推奨する方法でもある.
上記の方法の実行結果:
Javaでは,列挙は天然に単例モードを実現した.その構造方法は一度だけインスタンス化されます.
1.餓漢モード
餓漢モードは、最初から静的オブジェクトが作成され、その後もオブジェクトが存在します.このモードではスレッドセキュリティの問題はありません.
package com.dhb.builder.singleton;
public class Singleton1 {
private static Singleton1 instance = new Singleton1();
private Singleton1() {
}
public static Singleton1 getInstance() {
return Singleton1.instance;
}
}
2.怠け者モード
餓漢モードでは、簡単な実現が利点です.しかし、instanceはSingleton 1がロードされるとstaticが存在する静的メソッド領域に作成されるという問題がある.データベース接続プールを実装するなど、この方法ではデータベースの接続リソースを作成し、実際のシステムでは使用しません.これは資源の浪費をもたらした.そのため、このような状況に対して、それに対応する怠け者パターンが現れた.すなわち、最初はオブジェクトを作成せず、必要に応じてnewします.
package com.dhb.builder.singleton;
public class SingletonDemo1 {
private static SingletonDemo1 instance = null;
private SingletonDemo1() {
}
/**
*
* @return
*/
public static SingletonDemo1 getInstance() {
if(instance == null) {
instance = new SingletonDemo1();
}
return instance;
}
}
これはみんなが考えている最もよく使う怠け者のパターンの書き方です.しかし問題が来て、上記のモードはマルチスレッドの場合はスレッドが安全ではありません!すなわち,2つのスレッドがある場合,getInstance()と同時にinstanceの値がnullであると判断する.この場合、複数のインスタンスが作成されます.上記の問題を解決するために、ロックを導入しました.
package com.dhb.builder.singleton;
public class SingletonDemo2 {
private static SingletonDemo2 instance = null;
private SingletonDemo2() {
}
/**
* ,
* @return
*/
public static synchronized SingletonDemo2 getInstance() {
if(instance == null) {
instance = new SingletonDemo2();
}
return instance;
}
}
上記の方法では、スレッドセキュリティの問題は確かに解決されましたが、より悪い問題をもたらしました.それは、リクエストごとにロックがかかっていることです.これにより、パフォーマンスに深刻な影響を及ぼします.より良い方法は、二重検査メカニズムを採用することです.
package com.dhb.builder.singleton;
public class SingletonDemo3 {
private static SingletonDemo3 instance = null;
private SingletonDemo3() {
}
/**
* , synchronized
* @return
*/
public static SingletonDemo3 getInstance() {
if(instance == null)
synchronized (SingletonDemo3.class) {
if (instance == null) {
instance = new SingletonDemo3();
}
}
return instance;
}
}
上記の単一の例は、クラスの初期化には時間がかかるという問題があり、同時に2つのスレッドがgetInstanceメソッドに同時に進入し、最初のスレッドがロックされた後、2番目のスレッドが空ではないと判断した場合、instalceを直接使用し、このとき最初のスレッドがSingletonDemo 3オブジェクトに対してまだインスタンス化されていない場合、そのオブジェクト内部に時間のかかる参照が存在する場合、データベース接続の場合、2番目のスレッドで使用されるオブジェクトが不完全になります.空のポインタが表示されます.だからもっと良い書き方はvolatileを加えることです.happen-beforeの原則を保証します.
package com.dhb.builder.singleton;
public class SingletonDemo4 {
private volatile static SingletonDemo4 instance = null;
private SingletonDemo4() {
}
/**
* , synchronized
* @return
*/
public static SingletonDemo4 getInstance() {
if(instance == null)
synchronized (SingletonDemo4.class) {
if (instance == null) {
instance = new SingletonDemo4();
}
}
return instance;
}
}
このようにして、単一のモードが完全に解決されます.上記の方法は比較的に冗長で、もっと良い解決方法があるかどうか、幸いにも『effective java』という本を読んだことがあって、単例に対してもっと良い解決方法があります.
3.より良い解決方法
1つ目の方法は、静的内部クラスを使用することです.
package com.dhb.builder.singleton;
import java.util.stream.IntStream;
public class SingletonHolder {
private SingletonHolder() {
}
private static class InstanceHolder{
private final static SingletonHolder INSTANCE = new SingletonHolder();
}
public static SingletonHolder getInstance() {
return InstanceHolder.INSTANCE;
}
public static void main(String[] args) {
IntStream.rangeClosed(1,100).forEach(i -> new Thread(String.valueOf(i)){
@Override
public void run() {
System.out.println(SingletonHolder.getInstance());
}
}.start());
}
}
上記の方法の実行結果:
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
SingletonHolderクラスは一度だけインスタンス化されていることがわかります.この方法は内部クラスを巧みに利用し,簡単なコードで単一例を実現し,スレッドセキュリティである.
方法2:『effective java』にはもっと簡単な書き方があります.それは列挙です.『effective java』の著者が最も推奨する方法でもある.
package com.dhb.builder.singleton;
import java.util.stream.IntStream;
public class SingletonEnum {
private SingletonEnum() {
}
private enum Singleton {
INSTANCE;
private final SingletonEnum instance;
Singleton() {
instance = new SingletonEnum();
}
public SingletonEnum getInstance() {
return instance;
}
}
public static SingletonEnum getInstance() {
return Singleton.INSTANCE.getInstance();
}
public static void main(String[] args) {
IntStream.rangeClosed(1,100).forEach(i -> new Thread(String.valueOf(i)){
@Override
public void run() {
System.out.println(SingletonEnum.getInstance());
}
}.start());
}
}
上記の方法の実行結果:
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
Javaでは,列挙は天然に単例モードを実現した.その構造方法は一度だけインスタンス化されます.