1.单例模式简介

单例模式的类型

单例模式分为4中类型:饿汉式,懒汉式,静态内部类懒汉模式,枚举模式

2.饿汉模式

饿汉模式在类加载时就创建好类的实例对象,获取时直接返回已经创建好的对象,由于提前准备好了需要返回的对象,在调用时性能较好

public class Singleton {
	//类加载时就创建好类实例对象
    private static Singleton singleton = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return singleton;
    }
}

3.懒汉模式

懒汉模式在第一次被调用的时候,才会创建类的实例对象,节省内存

以下摘抄自 --- 《java并发编程实战》

竞态条件:当某个计算的正确性取决于多个线程的交替执行时序时,那么就会发生竞态条件。

换句话说,就是正确的结果要取决于运气。

最常见的静态条件类型就是“先检查后执行”操作,即通过一个可能失效的观测结果来决定下一步的动作

/**
 * 该懒汉单例模式有线程安全问题
 */
public class Singleton {

    private static Singleton singleton = null;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            //在这里,可能多个线程都通过了判空条件,都对singleton进行了初始化。各个线程会的到不同的对象,违反了单例模式的初衷。
            singleton = new Singleton();
        }
        return singleton;
    }
}
代码改进(线程安全)

由于未加锁的懒汉模式有线程安全问题,所以对以上代码进行加锁改进

public class Singleton {

    private static Singleton singleton = null;

    private Singleton() {
    }

    //加锁后,所有线程在进入该方法时都要执行加锁和解锁的动作,效率较低
    public static synchronized Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}
代码改进(性能优化)

该方式也叫 双重锁懒汉模式

public class Singleton {
 	/**
     * 由于jvm存在乱序执行功能,如果不加volatile修饰,也会出现线程不安全的情况
     * <p>
     * singleton = new Singleton();分为3个步骤
     * 1.在堆内存开辟内存空间,此时singleton的参数都是默认值
     * 2.初始化singleton的参数
     * 3.将对象指向内存空间
     * <p>
     * 如果不加volatile修饰符,实例化的步骤可能会被重排序为 1,3,2
     * <p>
     * 假如线程1 第一个进入该方法,按照重排序的步骤执行到3时,
     * 此时切换到线程2,判断singleton是否为空时,由于singleton已经指向了内存空间,所以该对象不为空,
     * 直接return的对象内的成员变量可能没有经过初始化。
     **/
    private volatile static Singleton singleton = null;

    private Singleton() {
    }

    public static Singleton getInstance() {
		//第一次判断 是为了筛除不必要的加锁和解锁动作
        if (singleton == null) {
            synchronized (Singleton.class) {
                //第二次判断 是为了线程安全
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }

        }
        return singleton;
    }
}

4.静态内部类模式

静态内部类模式利用了类加载过程的安全性,使用内部类创建单例对象

public class Singleton {

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

    private Singleton() {
    }

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

静态内部类是线程安全的懒加载单例模式,但是静态内部类模式也有致命的缺陷,无法传参数

5.枚举模式

public enum SingletonEnum {
    SINGLETON("ONE");

    SingletonEnum(String arg) {
		this.arg = arg;
    }
}