单例模式的七种写法,你知道几种?

3,287 阅读2分钟

前言

单例模式是最常用、也最简单的设计模式,但是要写出高性能并且安全单例模式确很困难。面试的时候,也常问你能写出几种单例模式,并说出其优缺点。今天就一起来盘点一下单例模式所有写法。

1. 饿汉模式

public class Singleton {

    private static final Singleton instance = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return instance;
    }

}

优点是线程安全的,只有一个实例。缺点是即使没有调用getInstance()方法,instance实例在类加载时被初始化了。

2. 懒汉模式,非线程安全

public class Singleton {

    private static Singleton instance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

}

虽然代码判断了当instance不等于空时,才进行初始化。但是当多个线程同时执行到if(instance==null)的时候,一个线程执行完new Singleton(),其他线程也会接着执行,导致new出多个实例。

3. 懒汉模式,线程安全

public class Singleton {

    private static Singleton instance;

    private Singleton() {
    }

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

}

在getIntance()方法上增加了synchronized锁,可以实现只有一个线程进行实例化对象,但是instance已经被实例化过,还是只能有一个线程访问getInstance()方法,性能急剧下降。

4. 双重检查锁模式,非线程安全

public class Singleton {

    private static Singleton instance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

}

虽然经过了两次判空,保证每次只有一个线程执行new Singleton(),但还是有问题。因为instance = new Singleton()这条语句并不是原子的,而是包含了三个步骤:

1. 栈上分配内存空间

2. 堆上初始化对象

3. 栈上引用指向堆内存空间地址

jvm为了提升执行效率,会对这三个步骤重排序。单线程不会有问题,多线程的时候,其中一个线程的步骤2和3交换后,其他线程会判断intance不会空,当使用instance对象的时候又会报空指针异常。

5. 双重检查锁模式,线程安全

public class Singleton {

    private static volatile Singleton instance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

}

与上一个不同的是instance对象用了volatile修饰,会禁止jvm对该对象的指令重排序,从而避免的上述问题。

6. 静态内部类

public class Singleton {

    private static class SingletonHolder {
        private static final Singleton instance = new Singleton();
    }

    private Singleton() {
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

这种方式是线程安全的,也不会有性能问题,也是《Effective Java》推荐的。

7. 枚举类

public enum  Singleton {

    INSTANCE;

}

最简单的一种,也是最推荐的一种。可以直接使用

Singleton instance = Singleton.INSTANCE;

是线程安全的,也不会有性能问题。