设计模式之单例模式
设计模式(Design pattern)是在某些特定场景下的最佳实践方案。下面简单介绍一下单例模式的创建方法以及原理
单例模式简介
单例模式:保证一个类只能被创建实例化一次。
单例模式的特点:
- 单例类只能拥有一个实例
- 单例类不能被new创建
- 单例类需要对外提供自身实例
单例模式类图
单例模式创建方式
下面所有的演示都以获取SingleObject类为例。
饿汉式创建
public class SingleObject {
private static SingleObject instance = new SingleObject();
private SingleObject(){}
public static SingleObject getInstance(){
return instance;
}
}
实现原理:static修饰的静态变量在类加载时初始化,类加载机制(双亲委派)保证了当前类创建的唯一机制。
优点:简单且安全建议使用
缺点:无论类是否使用都会加载到内存中。
懒汉式加锁机制
public class SingleObject {
public static SingleObject instance;
private SingleObject (){};
public static synchronized SingleObject getInstance() {
if(instance == null){
instance = new SingleObject ();
}
return instance;
}
}
实现原理:当前方式采用了synchronized 关键字修饰,被synchronized 修饰的方法会被加锁,实现同步代码块的效果,从而保证了创建对象的唯一性。
优点:类使用的时候才去创建类对象。
缺点:加锁力度大,针对的整个方法加锁。
为了解决上面加锁粒度大的问题下面提供一种双重校验创建单例的方法:
public class SingleObject {
public volatile static SingleObject instance;
private SingleObject (){};
public static SingleObject getInstance() {
if(instance == null){
synchronized(SingleObject .class){
//双重校验
if(instance == null){
instance = new SingleObject ();
}
}
}
return instance;
}
}
注意:此方法使用了synchronized和volatile 关键字配合使用,从而保证类创建的唯一性。当前方法解决了上面锁加锁粒度大的问题,但是也大大增加了代码的复杂性,并且引入了volatile 关键字才能保证类单例创建的安全性。
为什么需要volatile修饰?
volatile关键字意为可见性,实现代码的可见性,能够解决代码重排的问题。那么这块为什么需要引入呢,这块需要知道的是类instance = new SingleObject ()
创建的过程主要分为一下3步:
- 分配对象内存空间 memory = allocate
- 初始化对象 ctorInstance(memory)
- 设置instance 执行分配的内存地址instance= memory
在JVM编译时可能会出现指令重排的现象也就是说上面的步骤可能出现 1,3,2的情况。所以在当前特定的情况下 最外围的if(instance == null)
instance 此时可能不为null,但是当前实例有没有完全完成初始化,会导致使用报错。
静态内部类实现单例
public class SingleObject {
private static class singletonHolder{
private static SingleObject instance = new SingleObject ();
}
private SingleObject (){};
public static SingleObject getInstance() {
return singletonHolder.instance;
}
}
实现原理:当前思路也是通过JVM类加载机制保证类实例的唯一性,