1.单例设计模式是什么
单例设计模式就是一个类,只生成一个对象 ,给予其他类调用。
所以至少至少要保证三点:
-
构造器私有化。
-
类本身创建对象。
-
提供获取对象的方法
2.常见的几种单例创建方式
2.1 饿汉式
在类加载到内存,就实例化一个实例。
jvm保证线程安全简单实用,简单以用,线程安全。
缺点:不管是否用到,类加载时完成实例化了。有人说这个会影响程序启动时间,影响很小。
public class Singleton01 {
// static 变量,在类加载时,初始化变量
private static final Singleton01 instance = new Singleton01();
//私有构造器,让别人不能new
private Singleton01(){}
public static Singleton01 getInstance(){
return instance;
}
public static void main(String[] args) {
System.out.println(Singleton01.getInstance()==Singleton01.getInstance());
}
}
2.2 懒汉式 lazy loading(双重检查)
实现按需加载,同时带来了线程不安全的问题,。
解决线程安全(同时保证效率):双重检查(volatile synchronized),减小加锁范围synchronized,提高效率,同时线程安全。
缺点:代码变复杂了。
public class Singleton06 {
//双重检查volatile防止指令重排
private static volatile Singleton06 instance;
//私有构造器,让别人不能new
private Singleton06(){}
//双重检查实现线程安全、效率较高的单例模式
public static Singleton06 getInstance(){
if(instance == null){
synchronized(Singleton06.class){
if(instance == null) {
instance = new Singleton06();
}
}
}
return instance;
}
public static void main(String[] args) {
System.out.println(Singleton06.getInstance()== Singleton06.getInstance());
}
}
2.3 静态内部类
jvm保证单例, 加载外部类时不会加载内部类,这样就实现了懒加载,同时也保证了线程安全。
public class Singleton07 {
//私有构造器,让别人不能new
private Singleton07(){}
//静态内部类,初始化 new Singleton07();
//jvm只加载一次类,同时初始化静态变量
private static class Singleton07Holder{
private final static Singleton07 instance = new Singleton07();
}
public static Singleton07 getInstance(){
return Singleton07Holder.instance;
}
public static void main(String[] args) {
System.out.println(Singleton07.getInstance()== Singleton07.getInstance());
}
}
2.4 枚举实现
据说这种是最完美的一种。枚举解决线程同步,以及反序列化问题。
public enum Singleton08 {
INSTANCE;
public static void main(String[] args) {
System.out.println(Singleton08.INSTANCE== Singleton08.INSTANCE);
}
}
个人觉得:这四种都可以使用,推荐静态内部类实现,但是没有最好的,按需即可。
3.应用场景
只需要一个实例,秉承着多new一个实例,多浪费内存空间。
比如各种Manager、各种Factory。
我们开发中使用最多的是spring中ioc容器中的单实例bean的实例化。
在实例化对象之前,会锁住singletonObjects map对象,具体如下。
synchronized (this.singletonObjects) {}
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
//锁定singletonObjects对象
synchronized (this.singletonObjects) {
//从缓存中获取单例对象
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
//单例创建前的检查,并将其放入singletonsCurrentlyInCreation中
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
/**
* 核心: 从{@link org.springframework.beans.factory.ObjectFactory#getObject()}加载bean
*/
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
//单例创建后的检查,并将其从singletonsCurrentlyInCreation中移除
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
小小总结: 在开发过程中,只要不涉及自己重新搭建架构,基本上已经很少需要自己实例化单例了。spring已经帮我们完成了单实例的创建了。
面试必问设计模式,尽量从spring源码中重新整理一遍23种设计模式。有兴趣的同学可以看看我的源码分析系列,目前正在分析spring源码1、spring源码解析之概况流程。
未完待续。