提到常用的设计模式的时候,脑子中会蹦出很多设计模式,单例,工厂,监听者等等,经典的设计模式有23种,但是如果说对单例都不了解的话。说对其他设计模式有很深的见解就不会让人信服。现在我们就从单例聊一聊设计模式!
单例设计模式的概念
1. 一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。
单例设计模式的实现
单例模式的实现最常见的就是饿汉式和懒汉式。 但是其实除了这两种实现方式还有其他的实现方式!
- 饿汉式: 在类加载的时候,实例就会初始化完成。
public class Singleton {
private static final Singleton singleton = new Singleton();
private Singleton (){}
/**
* 获取实例对象
* @return 实例对象
*/
public static Singleton getInstance(){
return singleton;
}
}
- 懒汉式: 懒汉式相对于饿汉式的优势是支持延迟加载
/**
* 单例模式(懒汉式)
*/
public class Singleton {
private static Singleton singleton;
private Singleton (){}
/**
* 获取实例对象
* @return 实例对象
*/
public static Singleton getInstance(){
if (singleton == null) {
synchronized(Singleton.class) { // 此处为类级别的锁
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
-
tips: 这种实现方式有些问题。因为指令重排序,可能会导致还没来得及初始化,就被另一个线程使用了.解决办法是加上 volatile 关键字。实际上,高版本的 Java 已经在JDK 内部实现中解决了这个问题。
-
使用分析:
- 饿汉式,因为不支持延迟加载,会使实例占用资源多或初始化耗时长。
- 懒汉式,相对于饿汉式的优势是支持延迟加载。
- 比较, 个人比较倾向使用饿汉式。原因是,如果因为消耗资源或者加载资源消耗时间过长,会影响系统性能,导致超时,或者OOM,这样的情况在程序启动之初我们有办法进行解决,如果在生产中可能会导致生产事故。
- 静态内部类
/**
* 单例模式(静态内部类)
*/
public class Singleton {
private Singleton() {}
private static class SingletonHolder{
private static final Singleton instance = new Singleton();
}
/**
* @return 实例对象
*/
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
- 外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE,故而不占内存。这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
- 枚举(enum)
/**
* 单例模式(枚举)
*/
public enum Singleton {
INSTANCE;
}
单例模式的应用
这里主要说一下框架中单例模式是怎么实现的
- tips: Spring依赖注入Bean实例默认是单例的.
- Spring的依赖注入的AbstractBeanFactory 的 getBean中
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
接下来看下doGetBean(name, null, null, false);
/**
* 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
*/
@Nullable
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;
}
单例模的区别
- 线程内单例
- 在每个线程内都会创建一个新的实体,在java 中提供了ThreadLocal工具类。可轻松地实现线程唯一单例。
- 自己进行实现:
/**
* 线程内唯一
*/
public class Singleton {
private static final ConcurrentHashMap<Long, Singleton> instances
= new ConcurrentHashMap<>();
private Singleton(){}
/**
*
* @return 获取实例对象
*/
public static Singleton getInstance() {
Long currentThreadId = Thread.currentThread().getId();
instances.putIfAbsent(currentThreadId, new Singleton());
return instances.get(currentThreadId);
}
}
- 进程内单例
- 正常实现的单例类就是进程内唯一的。
- 集群环境单例
- 单例对象序列化并存储到外部共享存储区,比如redis或者文件