デザインパターンシリーズのシングルトンパターンについて語る

1. シングルトンモードの特徴

  • 建設業者の民営化

  • インスタンス化された変数参照はプライベート化されます。

  • インスタンスの取得方法は共通です

2. シングルトン モードのアプリケーション シナリオ

  • プログラム全体の実行において、クラスのインスタンスは 1 つだけ許可されます。

  • 頻繁にインスタンス化して破棄する必要があるオブジェクト。

  • 作成に時間やリソースがかかりすぎるが、頻繁に使用されるオブジェクト。

  • リソース間のコミュニケーションを促進する環境

3. Java でシングルトン モードを記述する N 通りの方法

これは最も簡単な方法ですが、欠点は、クラスがロードされるときにオブジェクトがインスタンス化されることです。

1) お腹を空かせた中華風

/**
 * 饿汉式
 */
public class EaterSingleton {
    private static final EaterSingleton INSTANCE = new EaterSingleton();

    private EaterSingleton() {
    }

    public static EaterSingleton getInstance() {
        return INSTANCE;
    }
}

2). 遅延モード - 二重検証

スレッドセーフ、遅延初期化。この方式では二重ロック機構を採用しているため、安全でマルチスレッド環境でも高いパフォーマンスを維持できます。

/**
 * 懒汉双重验证
 */
public class DoubleCheckSingleton {
    private  volatile static DoubleCheckSingleton INSTANCE = null;

    private DoubleCheckSingleton() {
    }

    public static DoubleCheckSingleton getInstance() {
        if (INSTANCE == null) {
            synchronized (DoubleCheckSingleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new DoubleCheckSingleton();
                }
            }
        }
        return INSTANCE;
    }
}

3). 静的内部クラス - シングルトン モード

JVM は単一のケースを保証し、外部クラスがロードされるときに内部クラスはロードされないため、遅延ロードを実現できます。

package com.bytearch.designPattern.singleton;

/**
 * 内部类
 * 加载外部类时不会加载内部类
 */
public class AnonymousInnerClsSingleton {

    private AnonymousInnerClsSingleton() {
    }

    private static class LazyHolder {
        private final static AnonymousInnerClsSingleton INSTANCE = new AnonymousInnerClsSingleton();
    }

    public static AnonymousInnerClsSingleton getInstance() {
        return LazyHolder.INSTANCE;
    }
}

4). シングルトンモードを列挙する

スレッドの同期を解決できるだけでなく、逆シリアル化も防ぐことができます。列挙型シングルトン パターンは「Effective Java」で推奨されています

package com.bytearch.designPattern.singleton;

/**
 * 内部枚举类单例模式
 */
public class Singleton {
    private Singleton() {
    }

    /**
     * 静态枚举
     */
     enum SingletonEnum {

        INSTANCE;

        private Singleton singleton;

        SingletonEnum() {
            singleton = new Singleton();
        }

        private Singleton getInstance() {
            return singleton;
        }
    }

    public static Singleton getInstance() {
        return SingletonEnum.INSTANCE.getInstance();
    }

    /**
    * 测试
    * @param args
    */
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(()-> {
                System.out.println(Singleton.getInstance().hashCode());
            }).start();
        }
    }
}

5. コンテナシングルトンパターン

/**
 * 容器式单例模式
 */
public class ContainerSingleton {
    private ContainerSingleton() {
    }
    private static Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

    public static Object getBean(String className) {
        Object singletonObject = singletonObjects.get(className);
        if (singletonObject == null) {
            synchronized (singletonObjects) {
                singletonObject =  singletonObjects.get(className);
                if (singletonObject == null) {
                    try {
                        try {
                            singletonObject = Class.forName(className).newInstance();
                        } catch (InstantiationException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                        singletonObjects.put(className, singletonObject);
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return singletonObject;
    }

}

Spring フレームワークでは、コンテナー スタイルのシングルトン パターンが使用されます。

Spring ソース org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java を見てみましょう。

/**
  * Return the (raw) singleton object registered under the given name.
  * <p>Checks already instantiated singletons and also allows for an early
  * reference to a currently created singleton (resolving a circular reference).
  * @param beanName the name of the bean to look for
  * @param allowEarlyReference whether early references should be created or not
  * @return the registered singleton object, or {@code null} if none found
  */
 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  Object singletonObject = this.singletonObjects.get(beanName);
  if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
   synchronized (this.singletonObjects) {
    singletonObject = this.earlySingletonObjects.get(beanName);
    if (singletonObject == null && allowEarlyReference) {
     ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
     if (singletonFactory != null) {
      singletonObject = singletonFactory.getObject();
      this.earlySingletonObjects.put(beanName, singletonObject);
      this.singletonFactories.remove(beanName);
     }
    }
   }
  }
  return (singletonObject != NULL_OBJECT ? singletonObject : null);
 }

4. まとめ

  • シングルトン モードには多くの「バリアント」があります。上記は、要約された優れた実装の一部です (そして、それらはすべてスレッドセーフです)。友達がいる場合は、たくさんある場合にどれを使用するべきか疑問に思うかもしれません。実際にはどれも使えます。趣味にもよりますが、仕事では前者の「飢えた中華風」の方がよく使われるかもしれません。個人的には「静的内部クラスシングルトンモード」がオススメです。

  • 上記のことから、「コンテナ型シングルトン モード」も「怠け者モード - 二重検証」の変形であることは誰でも簡単にわかります。

たとえば、軽量ソケット接続プールの実装では「コンテナ シングルトン パターン」も使用します。

public class ConnectionPool {
    /**
     * key is ip:port, value is ConnectionManager
     */
    private final static ConcurrentHashMap<String, ConnectionManager> CP = new ConcurrentHashMap<String, ConnectionManager>();

    public static Connection getConnection(InetSocketAddress socketAddress) throws MyException {
        if (socketAddress == null) {
            return null;
        }
        String key = getKey(socketAddress);
        ConnectionManager connectionManager;
        connectionManager = CP.get(key);
        if (connectionManager == null) {
            synchronized (ConnectionPool.class) {
                connectionManager = CP.get(key);
                if (connectionManager == null) {
                    connectionManager = new ConnectionManager(socketAddress);
                    CP.put(key, connectionManager);
                }
            }
        }
        return connectionManager.getConnection();
    }
    
}

5.サポート

このコンテンツがあなたにとって非常に刺激的だと思われる場合は、2 つの小さなお願いをしたいと思います。

  1. [視聴/転送]をクリックすると、より多くの人がこのコンテンツを閲覧できるようになります (気に入るかどうかに関係なく、[視聴/転送] をクリックしてください。これはすべてフーリガン行為です??)

  2. 公式アカウントの【構造について話す】に注目すると、公式アカウントの背景が下記の「番号」または「キーワード」を返信し、あなたに合った教材一式をお送りします。

    101: Java初級

    102: 上級 Java 上級

    103: ジャワインタビュー

建築について語る

おすすめ

転載: blog.csdn.net/weixin_38130500/article/details/106632441