浅谈设计模式系列之单例模式

1. 单例模式特点

  • 构造方法私有化

  • 实例化的变量引用私有化;

  • 获取实例的方法共有

2. 单例模式应用场景

  • 整个程序的运行中只允许有一个类的实例;

  • 需要频繁实例化然后销毁的对象。

  • 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。

  • 方便资源相互通信的环境

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. 总结

  • 单例模式有很多"变种",以上是总结的几种比较好的实现(也都是线程安全的),有小伙伴就疑问了,这么多该使用哪一种呢? 其实都可以使用,可以看自己爱好,可能工作上用得比较多的是第一种"饿汉式",个人比较推荐"静态内部类-单例模式",

  • 以上大家很容易 发现 "容器式单例模式 " 也是 "懒汉模式-双重验证" 的变种,实际场景上我们完全可以根据需要适当定制,做到活学活用。

例如我在轻量级socket连接池实现中也使用了“容器式单例模式”。

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、 支持

如果你觉得这篇内容对你挺有启发,我想请你帮二个小忙:

  1. 点个 [在看/转发],让更多的人也能看到这篇内容 (喜欢不点在看/转发,都是耍流氓 ????)

  2. 关注公众号 [浅谈架构], 公众号后台回复以下"编号" 或 "关键字",送你全套适合你的学习资料.

    101: Java初级

    102: Java高级进阶

    103: Java面试

浅谈架构

猜你喜欢

转载自blog.csdn.net/weixin_38130500/article/details/106632441