一、概述
数学与逻辑学中,singleton定义为“有且仅有一个元素的集合”。
单例模式最初的定义出现于《设计模式》(艾迪生维斯理, 1994):“保证一个类仅有一个实例,并提供一个访问它的全局访问点。”
Java中单例模式定义:“一个类有且仅有一个实例,并且自行实例化向整个系统提供。”
要点:
- 1、单例类只能有一个实例;
- 2、单例类必须自行创建这个实例;
- 3、单例类必须自行向整个系统提供这个实例。
二、代码实现
2.1 双重检验锁方式
/**
* 双重检验锁方式
* @author xch
* 2023/2/2 10:59
*/
public class Singleton {
private static volatile Singleton instance;
/**
* 私有,防止其他类通过new创建
*/
private Singleton() {}
/**
* 懒加载
* 线程安全
* @return Singleton
*/
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
2.2 静态内部类方式
/**
* 静态内部类方式
* @author xch
* 2023/2/2 10:59
*/
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
/**
* 私有,防止其他类通过new创建
*/
private Singleton() {}
/**
* 此方法通过调用内部静态类,才会实例化intance
* 懒加载
* 线程安全
* 缺点:不能传参
* @return Singleton
*/
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
2.3 枚举类方式
/**
* 枚举类
* @author xch
* 2023/2/2 10:59
*/
public enum Singleton {
/**
* 绝对防止反序列化创建新对象
* 饿汉式
* 线程安全
*/
INSTANCE
}
三、优缺点
3.1 优点
1、实例控制
单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
2、灵活性
因为类控制了实例化过程,所以类可以灵活更改实例化过程。
3.2 缺点
1、开销
虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
2、可能的开发混淆
使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
四、源码实例
4.1 JDK的Runtime类
/**
* 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.
*
* @author unascribed
* @see java.lang.Runtime#getRuntime()
* @since JDK1.0
*/
public class Runtime {
private static Runtime currentRuntime = new Runtime();
/**
* Returns the runtime object associated with the current Java application.
* Most of the methods of class <code>Runtime</code> are instance
* methods and must be invoked with respect to the current runtime object.
*
* @return the <code>Runtime</code> object associated with the current
* Java application.
*/
public static Runtime getRuntime() {
return currentRuntime;
}
/** Don't let anyone else instantiate this class */
private Runtime() {}
}
4.2 Spring的Bean默认是单例,下面是Spring怎么获取单例Bean的代码
Spring的依赖注入(包括lazy-init方式)都是发生在AbstractBeanFactory的getBean里。
doGetBean方法调用getSingleton进行bean的创建。同样使用了双重检验锁方式!
/**
* 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;
}