2019年12月17日

承接上文​​《设计模式学习(五):单例模式 (上)》​​,继续设计模式学习,补充“单例模式”的三种方法,分别是:双锁校验、内部类、枚举类。

目录

​4、双重校验​

​5、内部类​

​6、枚举类​


4、双重校验

volatile防止指令重排
synchronized对象锁解决并发问题
package design.SinglePattern;

/**
* 双锁检验
* 双重非空判断,new对象前加一次锁。
* volatile关键字,考虑的是,new关键字在虚拟机中执行时其实分为很多步骤,具体原因可以参考深入理解java虚拟机一书(考虑的是这个new关键字字节码执行时是非原子性的),而volatile关键字可以防止指令重排。
* @author moubin.mo
* @date: 2019/12/17 16:11
*/

public class SingletonCase4 {

/**volatile防止指令重排*/
private static volatile SingletonCase4 singleton;

public SingletonCase4() {
}

/**
* 只是在实例为空时才进行同步创建
* 为什么做了2次判断?
* A线程和B线程同时进入方法: getInstance()
* 然后都在1位置处判断了实例为null
* 然后都进入了同步块2中
* 然后A线程优先进入了同步代码块2中(B线程也进入了),然后创建了实例
* 此时,如果没有3处的判断,那么A线程创建实例同时,B线程也会创建一个实例
* 所以,还需要做2次判断
* */
public static SingletonCase4 getInstance(){
if (singleton == null){ // 1
synchronized (SingletonCase4.class){ // 同步块2
if (singleton == null){ // 3
singleton = new SingletonCase4();
}
}
}
return singleton;
}

}

 

5、内部类

  • 实现内部类的懒加载
  • 内部类的属性为静态变量的初始化,因此不存在线程安全问题
  • 调用构造方法时,外部类被加载,但这时其静态内部类其实未被加载;直到调用该内部类的静态方法时,内部类的属性才被加载。
package design.SinglePattern;

/**
* 内部类
* 优点:由于静态内部类跟外部类是平级的,所以外部类加载的时候不会影响内部类,因此实现了lazy loading, 同时也是利用静态变量的方式,使得INSTANCE只会在SingletonHolder加载的时候初始化一次,从而保证不会有多线程初始化的情况,因此也是线程安全的。
* @author moubin.mo
* @date: 2019/12/17 16:11
*/

public class SingletonCase5 {
//静态内部类,在被用到时才加载(根据内部类不会在其外部类被加载的同时被加载的事实)
private static class SingletonCase5Holder{
private static final SingletonCase5 singleton = new SingletonCase5();
}

public SingletonCase5() {
}

public static SingletonCase5 getInstance(){
return SingletonCase5Holder.singleton;
}

}

 

6、枚举类

  • 枚举类的属性为一个懒加载的私有变量
  • 首先我们分析一下,为什么枚举类可以保证线程安全?原因是:经过反编译,枚举类内部是执行了静态代码块,和饿汉式代码有异曲同工之妙,前面我们分析了当一个Java类第一次被真正使用到的时候静态资源被初始化、Java类的加载和初始化过程都是线程安全的。所以,创建一个enum类型是线程安全的。
package design.SinglePattern;

/**
* 枚举类
* 本质就是:创建一个枚举类,封装一个对象,枚举类私有构造器中初始化对象
* @author moubin.mo
* @date: 2019/12/17 16:11
*/

public class SingletonCase6 {
static enum SingletonEnum {
//创建一个枚举对象,该对象天生为单例
INSTANCE;
private SingletonCase6 singleton;

//私有化枚举的构造函数
private SingletonEnum() {
System.out.print("-----1-----");
singleton = new SingletonCase6();
}

public SingletonCase6 getSingleton() {
System.out.print("-----2-----");
return singleton;
}
}

private SingletonCase6() {
}

public static SingletonCase6 getInstance(){
System.out.print("-----3-----");
return SingletonEnum.INSTANCE.getSingleton();
}

public static void main(String[] args) {
getInstance();
}
}

设计模式学习(五):单例模式 (下)_枚举类

欢迎扫二维码关注公众号,获取技术干货