单例模式的五种实现方式

单例模式就是确保一个类只有一个实例,并提供全局的访问点。

1. 饿汉式

一上来就直接创建一个实例,不存在线程安全的问题。

package com.xqm.singleton;

public class Singleton1 {

    private static Singleton1 instance = new Singleton1();

    // 对外提供的静态方法
    public static Singleton1 getSingleton(){
        return instance;
    }

}

class SingletonTest{
    public static void main(String[] args) {
        // 虽然创建了两个实例,但是都是指向同一个堆内存
        Singleton1 singleton1=Singleton1.getSingleton();
        Singleton1 singleton2=Singleton1.getSingleton();
    }
}

2.懒汉式

懒汉式不像饿汉式,而是等到需要的时候再创建。

package com.xqm.singleton;

public class Singleton2 {

    private static Singleton2 instance;

    // 提供一个静态的方法,等到使用的时候再创建
    public static synchronized Singleton2 getSingleton() {
        if (instance == null) {
            instance = new Singleton2();
        }
        return instance;
    }


}

class Singleton2Test {
    public static void main(String[] args) {
        // 创建实例
        Singleton2 singleton = Singleton2.getSingleton();
    }
}

懒汉式存在线程安全问题,假如有两个线程Thread1,Thread2同时使用getSingleton,Thread1,Thread2同时到达if判断语句,这时会创建两个实例,造成线程不安全。因此可以给getSingleton上线程锁,但是会造成效率低。

3.双重校验

是在懒汉式上的改进。采用DCL的方式。

package com.xqm.singleton;

public class Singleton3 {
	// volatile解决指令重排的问题,因为new instance是分为三步的
    private volatile static Singleton3 instance;

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

instance = new Singleton();分为三步:

  1. 给instance分配内存;
  2. 调用 instance 的构造函数来初始化成员变量;
  3. 将给instance对象指向分配的内存空间(此时singleton才不为null)

4.静态内部类

package com.xqm.singleton;

public class Singleton4 {

    // 静态内部类
    private static class SingletonInstance {
        private static final Singleton4 insatance = new Singleton4();
    }

    public static Singleton4 getInstance(){
        return SingletonInstance.insatance;
    }

}

5.枚举

枚举元素本身就是单例的,可以防止反射和反序列化。

package com.xqm.singleton;

public enum  Singleton5 {
    INSTANCE;

    public void doSomething(){
        System.out.println("自己操作");
    }
}

class Singleton5Test{
    public static void main(String[] args) {
        // 直接通过这个方式调用
        Singleton5.INSTANCE.doSomething();
    }
}