简介
单例模式是23种设计模式中比较基础的设计模式了。什么是单例呢,就是全局唯一的一个实例,一个类只有一个实例,只能通过唯一的入口来获取实例。
下面就跟大家分享下7种实现方式
饿汉式
顾名思义,取名叫饿汉式,就是一开始创建实例。
public class Hungry {
private static final Hungry instance = new Hungry();
private Hungry() {}
public static Hungry getInstance() {
return instance;
}
}
优点: 实现简单,并且是线程安全的。
缺点: 在类加载的时候就初始化实例了,如果程序很久才使用该实例或者一直没有使用的话,该实例会一直在内存中,占用内存。所以有了下面的懒汉式。
懒汉式
懒汉式,就是延迟加载,在我们需要使用单例的实例的时候再创建。
public class Lazyload {
private static Lazyload instance = null;
private Lazyload() {}
public static Lazyload getInstance() {
if (null == instance) {
instance = new Lazyload();
}
return instance;
}
}
优点: 实现简单,并且是延迟加载的,在我们需要使用单例的实例调用getInstance方法的时候再创建。
缺点: 线程不安全的,如果同时有线程1和线程2调用getInstance方法,都判断instance为空,这个时候线程1和线程2都会创建一个实例,从而不能保证实例唯一性,因为线程1和线程2都是用的自己创建的实例。
懒汉式升级版(同步)
懒汉式,就是延迟加载,在我们需要使用单例的实例的时候再创建。
public class Lazyload {
private static Lazyload instance = null;
private Lazyload() {}
// 同步方法
public synchronized static Lazyload getInstance() {
if (null == instance) {
instance = new Lazyload();
}
return instance;
}
}
优点: 实现简单,并且是延迟加载的,在我们需要使用单例的实例调用getInstance方法的时候再创建,并且解决了线程安全问题,保证了实例的唯一性。
缺点: 虽然是线程安全的,但是synchronized关键字修饰的getInstance同一时间只能有一个线程可以访问,其他线程都要等待,会影响性能。
双重校验锁
为了解决上面一种方式的问题,设计思想是对instance实例进行两次检查,第一次判断instance是否为空,如果为空再加锁进行为空判断,如果还是为空就创建实例。这种方式可以提高性能,因为每个线程都可以访问getInstance方法。
public class DoubleCheck {
private static DoubleCheck instance = null;
private DoubleCheck() {}
public static DoubleCheck getInstance() {
if (null == instance) {
synchronized (DoubleCheck.class) {
if (null == instance) {
instance = new DoubleCheck();
}
}
}
return instance;
}
}
缺点: 这个方式看似很完美了,但是还是会有隐藏问题,为什么呢,我们来看看instance = new DoubleCheck()这行代码,这个初始化并非原子性操作,而是有3步:
- 给instance分配内存
- 调用DoubleCheck构造方法初始化
- 将instance对象的引用指向分配的内存空间(一旦执行这一步,instance就不是null了)
但是JVM在即时编译的时候存在指令重排序的优化,那么上面的第二和第三步就有可能交换顺序,则执行顺序有可能是1-2-3,也有可能是1-3-2,如果是1-3-2的话,假如线程1在第三步执行完成过后,线程2刚好正要执行第一次(第6行)的null == instance判断,这个时候线程2发现instance已经不为空了,那么就直接将instance返回使用了,但是线程2使用的是还没有初始化的instance实例。
双重校验锁升级版(volatile)
解决双重校验的指令重排序的问题,可以使用volatile关键字,来防止指令重排序,那么优化后的代码如下
public class DoubleCheck {
private static volatile DoubleCheck instance = null;
private DoubleCheck() {}
public static DoubleCheck getInstance() {
if (null == instance) {
synchronized (DoubleCheck.class) {
if (null == instance) {
instance = new DoubleCheck();
}
}
}
return instance;
}
}
静态内部类
public class Singleton {
private Singleton() {}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
private static Singleton INSTANCE = new Singleton();
}
}
优点: 延迟加载,当调用getInstance方法才会加载私有内部类进行实例化,并且是线程安全的,性能高。建议使用。
枚举
public enum SingletonEnum {
INSTANCE;
public static SingletonEnum getInstance() {
return INSTANCE;
}
}
或者
public class SingletonEnum {
private SingletonEnum() {
}
public static SingletonEnum getInstance() {
return HolderEnum.INSTANCE.getInstance();
}
private enum HolderEnum {
INSTANCE;
private SingletonEnum instance;
HolderEnum() {
this.instance = new SingletonEnum();
}
private SingletonEnum getInstance() {
return instance;
}
}
}
创建枚举类默认就是线程安全的,而且还能防止反序列化导致重新创建新的对象。也是建议使用的。
以上就是7中单例模式的实现方式,如果有理解或者写的不对的地方,欢迎指出,共同学习!