为了知道大牛是如何应用设计模式的,我喜欢扒一扒知名项目中的源码。

 

单例模式的使用场景其实还挺简单,就是一个类只允许创建一个对象,全局共享使用这个对象。


在 Java 中实现单例,需要考虑是否懒加载、是否线程安全的问题,实现方式:饿汉式、懒汉式、双重检查、静态内部类、枚举。具体实现代码

 

开源使用实例一

JDK 中 java.lang.Runtime 类,每个运行中的 Java 应用的环境信息,单例。


看下它的注释:

 * Every Java application has a single instance of class

 * <code>Runtime</code> that allows the application to interface with

 * the environment in which the application is running. The current

 * runtime can be obtained from the <code>getRuntime</code> method.

 * <p>

 * An application cannot create its own instance of this class.

 

看下它的部分源码:

public class Runtime {    private static Runtime currentRuntime = new Runtime();
   public static Runtime getRuntime() {        return currentRuntime;    }
   private Runtime() {}    .    .    .}

这是单例模式经典的实现方式之一:饿汉式

 

开源使用实例二

Spring 的单例 bean 的实现。Spring 的单例是 IoC 容器级别的,这句话啥意思呢?

ApplicationContext context = new ClassPathXmlApplicationContext("beanFactoryTest.xml");MyTestBean bean1 = (MyTestBean)context.getBean("myTestBean");MyTestBean bean2 = (MyTestBean)context.getBean("myTestBean");System.out.println(bean1 == bean2); // 打印true ApplicationContext context2 = new ClassPathXmlApplicationContext("beanFactoryTest.xml");MyTestBean bean3 = (MyTestBean)context2.getBean("myTestBean");System.out.println(bean1 == bean3); // 打印false
代码中 bean1 = bean2,因为 Spring 默认是单例,且 bean1 和 bean2 都取自 context 实例中容器;bean1 != bean3,因为 bean1 和 bean3 分别取自不同的容器。

 

那 Spring 是如何实现单例的呢?

看下 org.springframework.beans.factory.support.AbstractBeanFactory 获取 bean 的代码

protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {    .    .    .    //获取缓存的 bean 实例    Object sharedInstance = getSingleton(beanName);    .    .    .    //创建 bean 实例  if (mbd.isSingleton()) {    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {      @Override      public Object getObject() throws BeansException {        try {          return createBean(beanName, mbd, args);        }        catch (BeansException ex) {          // Explicitly remove instance from singleton cache: It might have been put there          // eagerly by the creation process, to allow for circular reference resolution.          // Also remove any beans that received a temporary reference to the bean.          destroySingleton(beanName);          throw ex;        }      }    });    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);  }    .    .    .
}
1、Srping IoC 容器在初始化的时候,若未配置懒加载,单例 bean 实例会在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory 创建,
createBean -> doCreateBean -> createBeanInstance 2、单例 bean 实例获取的逻辑在 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry 的 getSingleton 方法
public class DefaultSingletonBeanRegistry {    //缓存单例 bean 实例    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
   protected Object getSingleton(String beanName, boolean allowEarlyReference) {        //从 ConcurrentHashMap 中获取 bean 实例        Object singletonObject = this.singletonObjects.get(beanName);    .    .    .    return (singletonObject != NULL_OBJECT ? singletonObject : null);    }}

 

可见 Spring 的单例 bean 实例的创建,是通过代码自己控制的,已创建的单例 bean 实例保存在一个 ConcurrentHashMap 中,而不是上述单例模式的典型实现方式。
这里 Spring 的单例是在 IoC 容器中,实际开发中有时候还需要考虑线程间的单例和分布式中多进程间的单例。线程间的单例可以使用 ThreadLocal 实现,进程间的单例要借助分布式锁以及对单例实例进行序列化存储与反序列化。


更多相关文章

  1. JDK 中有哪些同步容器?并发容器?
  2. CopyOnWriteArrayList,一个面试中经常问到的冷门容器
  3. 解读容器的 2020:寻找云原生的下一站
  4. Spring Ioc 实例化 Bean 对象有几种方式?
  5. 静态路由实例
  6. 谷歌助力,快速实现 Java 应用容器化
  7. 原型模式 - 通过复制生成实例
  8. 单例模式 - 只有一个实例
  9. Tomcat 单机多实例部署

随机推荐

  1. Android gradle 命令行打包
  2. Android 的EditText实现不可编辑
  3. 【Android】【Lottie】在Android中使用Lo
  4. [导入]Android平台上四种保存数据的方法
  5. android获取屏幕分辨率大小(DisplayMetri
  6. 启动模式详解
  7. android HttpURLConnection 连接网络 读
  8. Android(安卓)获取并显示远程图片 Picass
  9. android——Bitmap.Config ARGB_8888
  10. Android学习札记22:ThumbnailUtils