什么是单例模式?
   概念:23种设计模式之一,通过单例模式的方法创建的类在整个应用程序中只有一个实例。
   核心思想:构造方法私有化
实现方式
 单利模式的实现方式有很多,在此简单介绍以下几种写法

   1. 饿汉式
   2.懒汉式
   3.双重检测锁模式
   4.静态内部类
   5.枚举类

   代码示例
饿汉式:类加载的时候就创建该类的唯一实例对象,天生线程安全。无论该对象是否被使用,在类加载的时候都会被创建,一定程度上导致内存资源的浪费。
 

//饿汉式单例
public class Hungry {
    private Hungry() {
    }

    private static final Hungry HUNGRY_SINGLETON = new Hungry();

    public static Hungry getInstance(){
        return HUNGRY_SINGLETON;
    }
}

懒汉式:实例对象被使用时才会被创建,实现了内存资源的合理分配。但多线程并发下会破坏这种单例,即线程不安全

//懒汉式单例
public class Lazy {
    private Lazy() {
    }

    private static Lazy lazySingleton = null;

    public static Lazy getInstance() {
        if (lazySingleton == null) {
            lazySingleton = new Lazy();
        }
        return lazySingleton;
    }
}

为什么上述懒汉式单例是线程不安全的呢,如下代码,在Lazy类的无参构造中加入一行打印并引入多线程场景进行测试

//懒汉式单例
public class Lazy {
    private Lazy() {
        System.out.println("线程:"+Thread.currentThread().getName()+"创建了对象");
    }

    private static Lazy lazySingleton = null;

    public static Lazy getInstance() {
        if (lazySingleton == null) {
            lazySingleton = new Lazy();
        }
        return lazySingleton;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                Lazy instance = lazySingleton.getInstance();
                System.out.println(instance);
            }).start();
        }
    }
}

运行结果:有5个线程执行了Lazy构造方法创建了5个实例对象(就本次运行结果而言),不符合单例模式的设计。那怎样解决这个问题,实现既能合理的分配内存资源又可以保证线程安全呢?于是就有了下面我们要讲的 双重检测锁模式

D:\installPath\Java\jdk1.8.0_121\bin\java.exe "-javaagent:D:\installPath\IntelliJ IDEA 
线程:Thread-0创建了对象
线程:Thread-4创建了对象
线程:Thread-3创建了对象
线程:Thread-1创建了对象
线程:Thread-2创建了对象
com.kang.singleton.Lazy@34a705cd
com.kang.singleton.Lazy@34a705cd
com.kang.singleton.Lazy@56d4b179
com.kang.singleton.Lazy@56d4b179
com.kang.singleton.Lazy@15c05ae4
com.kang.singleton.Lazy@45b30948
com.kang.singleton.Lazy@45b30948
com.kang.singleton.Lazy@14f482d7
com.kang.singleton.Lazy@14f482d7
com.kang.singleton.Lazy@14f482d7

Process finished with exit code 0

双重检测锁模式: 也就是我们所说的DCL懒汉式

//双重检测锁模式
public class DCL {
    private DCL() {
    }
    
    //因为 new DCL();不是一个原子操作,这里使用volatile修饰dclSingleton 防止指令重排
    private volatile static DCL dclSingleton = null;

    public static DCL getInstance() {
        if (dclSingleton == null) {
            synchronized (Lazy.class) {
                if (dclSingleton == null) {
                    dclSingleton = new DCL();//不是一个原子操作
                }
            }
        }
        return dclSingleton;
    }
}

引入同样的多线程测试场景进行测试

//双重检测锁模式
public class DCL {
    private DCL() {
        System.out.println("线程:" + Thread.currentThread().getName() + "创建了对象");
    }

    //因为 new DCL();不是一个原子操作,这里使用volatile修饰dclSingleton 防止指令重排
    private volatile static DCL dclSingleton = null;

    public static DCL getInstance() {
        if (dclSingleton == null) {
            synchronized (Lazy.class) {
                if (dclSingleton == null) {
                    dclSingleton = new DCL();//不是一个原子操作
                }
            }
        }
        return dclSingleton;
    }
    
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                DCL instance = dclSingleton.getInstance();
                System.out.println(instance);
            }).start();
        }
    }
}

运行结果:程序执行只创建了一个实例,符合预期结果(此处暂不考虑Java反射技术对单例模式的影响)

D:\installPath\Java\jdk1.8.0_121\bin\java.exe "-javaagent:D:\installPath\IntelliJ IDEA 
线程:Thread-0创建了对象
com.kang.singleton.Lazy@6b047ec
com.kang.singleton.Lazy@6b047ec
com.kang.singleton.Lazy@6b047ec
com.kang.singleton.Lazy@6b047ec
com.kang.singleton.Lazy@6b047ec
com.kang.singleton.Lazy@6b047ec
com.kang.singleton.Lazy@6b047ec
com.kang.singleton.Lazy@6b047ec
com.kang.singleton.Lazy@6b047ec
com.kang.singleton.Lazy@6b047ec

Process finished with exit code 0

静态内部类: 在类的一个生命周期中静态代码块只在类加载时执行一次,可以满足单例模式的设计要求(此处暂不考虑Java反射技术对单例模式的影响)

//静态内部类
public class Singleton {
    private Singleton() {
    }
	//提供一个公开的方法作为访问该唯一实例的入口
    public static Singleton getInstance(){
        return Test.SINGLETON;
    }
    
    //创建静态内部类Test
    public static class Test {
        private static final Singleton SINGLETON = new Singleton();
    }
}

枚举类: 枚举类型是Java 5中新增特性的一部分,也是类(class)的一种,自带单例模式,且可以防止Java反射技术对单例模式的破坏

//枚举类(enum)
public enum Enumeration {
    INSTANCE;

    public static Enumeration getInstance(){
        return Enumeration.INSTANCE;
    }
}