シングルトン モードは、怠け者モードとハングリーマン モードの 2 つのカテゴリに大別できますが、怠け者であるか空腹であるかを気にする必要はなく、それらの原則と違いを理解する必要があります。(怠け者とは、クラスがロードされた後、シングルトンがインスタンス化されず、最初の使用まで遅延されることを意味します。悪役とは、クラスがロードされ、シングルトンがインスタンス化されることです。)
この記事の例はすべて、スレッドセーフなシングルトン パターンです。
コードとコメントを直接見てください。
1. ダブルロック シングルトン モード (レイジーマン モード)
このモードは、コードが理解しやすく、ガベージ オブジェクトを生成しない遅延ロード モードであるため、作成者が以前に最もよく使用したモードでもあります。基本的なインタビューでは、このモードが最初に回答されます. このモードは、揮発性および同期ロックの役割を含む多くの知識ポイントを拡張できます.
/**
* 双层加锁的单例模式
* */
public class SingletonTest3 implements Test{
/**
* instance使用volatile修饰,防止线程之间出现脏数据,导致线程A已经执行完instance = new SingletonTest3() ;
* 后线程B拿到锁,但是判断instance还是null,而导致instance初始化两次。
*/
private volatile static SingletonTest3 instance = null ;
private SingletonTest3(){
Log.d("gggl" , "SingleTest3 初始化了") ;
}
public static SingletonTest3 getInstance(){
//首先进行instance的null判断,为了在没有线程竞争的情况下提高速度不必进行锁的竞争。
if (instance == null) {
//加锁防止线程竞争
synchronized (SingletonTest3.class) {
//二次判断,这里是必须的,因为第一个null判断不是线程安全的。
if (instance == null) {
instance = new SingletonTest3() ;
}
}
}
return instance ;
}
@Override
public void test() {
Log.d("gggl" , "SingletonTest3 初始化了") ;
}
}
2. static final はメンバー変数インスタンスを変更します (悪役モード)
このモードでは、クラスがメモリにロードされるときにコンストラクターが呼び出されます. シングルトン プロジェクトが開始された後、常にコンストラクターが必要な場合は、この方法で問題はなく、簡単で便利です. ただし、プロジェクトの開始後に特定のアクションによってシングルトンをトリガーする必要がある場合、この方法はお勧めできません。また、メモリが少し無駄になります。実はちょっとうるさいんですけど、これはインタビューなので真面目に。-^-
import com.example.interview.Test;
public class SingletonTest1 implements Test {
private SingletonTest1(){
Log.d("gggl" , "SingletonTest1 初始化了") ;
}
private static final SingletonTest1 instance = new SingletonTest1() ;
public static SingletonTest1 getInstance() {
return instance ;
}
public void test(){
Log.d("gggl" , "SingletonTest1 say hello!!!!") ;
}
}
3.静的インナークラスモード(レイジーマンモード)
静的内部クラス パターン。このモードは少し責任感があり、筆者は基本的に最初の二重無効判定モードを使用するため、好んで使用しません。しかし、面接に行く方法はありません。見るだけです。
public class SingletonTest1 implements Test {
private SingletonTest1(){
Log.d("gggl" , "SingletonTest1 初始化了") ;
}
public static SingletonTest1 getInstance() {
return SingletonHander.singleton ;
}
public void test(){
Log.d("gggl" , "SingletonTest1 say hello!!!!") ;
}
private static class SingletonHander{
private static final SingletonTest1 singleton = new SingletonTest1 ();
}
}
テストコード:
try {
Class.forName("com.example.interview.designpattern.SingletonTest1" , true , getClassLoader()) ;
Log.d("gggl" , "SingletonTest1加载完毕") ;
Class.forName("com.example.interview.designpattern.SingletonTest2" , true , getClassLoader()) ;
Log.d("gggl" , "SingletonTest2加载完毕") ;
Class.forName("com.example.interview.designpattern.SingletonTest3" , true , getClassLoader()) ;
Log.d("gggl" , "SingletonTest3加载完毕") ;
SingletonTest1.getInstance().test();
SingletonTest2.getInstance().test();
SingletonTest3.getInstance().test();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
ここではまず、SingletonTest のいくつかのクラスをロードします (注: Class. がこの段階で実行される場合)。次に、SingletonTest.getInstance().test() メソッドをそれぞれ呼び出します。実行結果を見てみましょう。
2022-09-16 10:16:01.081 11710-11710/com.example.interview D/gggl: SingletonTest1 初始化了
2022-09-16 10:16:01.081 11710-11710/com.example.interview D/gggl: SingletonTest1加载完毕
2022-09-16 10:16:01.082 11710-11710/com.example.interview D/gggl: SingletonTest2加载完毕
2022-09-16 10:16:01.082 11710-11710/com.example.interview D/gggl: SingletonTest3加载完毕
2022-09-16 10:16:01.082 11710-11710/com.example.interview D/gggl: SingletonTest1 say hello!!!!
2022-09-16 10:16:01.082 11710-11710/com.example.interview D/gggl: SingletonTest2 初始化了
2022-09-16 10:16:01.082 11710-11710/com.example.interview D/gggl: SingletonTest2 say hello!!!!
2022-09-16 10:16:01.082 11710-11710/com.example.interview D/gggl: SingleTest3 初始化了
2022-09-16 10:16:01.082 11710-11710/com.example.interview D/gggl: SingletonTest3 初始化了
You have seen that SingletonTest1 is the first type of Hungry Man mode (private static final SingletonInstance1 = new SingletonInstance1()) and the constructor is running when the class is loaded. これが違いであり、知識ポイントをインタビューします。
要約:
実際には、開発時に上記の 3 つのモードに 1 または 3 を使用するだけで済みます.2 番目のモードは遅延ロード モードではないため、使用しないようにしてください.良いコーディング習慣も少しずつ開発する必要があります. インタビューでは、上記の 3 つのモードを知っていれば十分ですが、スレッドセーフではない最も単純なモードについては、書き出さないでください。