【设计模式学习1】什么是单例模式?单例模式的几种实现。

一、什么是单例模式

单例模式是在内存中只创建一个对象的模式,它保证一个类只有一个实例。

二、单例模式的几种实现

(一)懒汉式单例模式

/**
 * 懒汉式单例模式
 * (懒加载,需要的时候在去加载)
 * 优点:需要SingleObject时,才会去实例化,节省空间
 * 缺点:在多线程环境下,getSingleObject() 方法并不是线程安全的
 */

public class SingleObject {
    //私有化对象(未实例化)
    private static SingleObject object;

    //私有化构造方法
    private SingleObject() {};

    //获取私有化对象
    public static SingleObject getSingleObject() {
        //bean为null才去实例化,否则直接返回。
        if (object == null) {
            object = new SingleObject();
        }
        return object;
    }
}

(二)饿汉式单例模式

/**
 * 饿汉式单例模式
 * (线程安全,但会造成资源浪费)
 * 优点:在类加载时候创建实例,不存在线程安全问题
 * 缺点:占用系统资源,如果这个实例没有被用到,则会造成资源浪费
 */
public class Single {
    //私有化对象(已实例化)
    private static Single object = new Single();

    //私有化构造方法
    private Single() {};

    //获取私有化对象
    public static Single getSingle() {
        return object;
    }

}

(三)双重校验加锁式单例模式(线程安全)

public class SingleObject{
    //volatile关键字防止指令重排序造成的空指针异常(通过插入特定的内存屏障的方式来禁止指令重排序)
    private static volatile SingleObject object;

    //私有构造方法
    private SingleObject() {}

    public static SingleObject getSingleObject() {
        //第一次检查防止每次获取bean都加锁
        if (object == null) {
            //加锁,防止第一次创建实例化时,并发线程多次创建对象
            synchronized (SingleObject.class) {
                //第二次检查判断对象没有实例化,则进行对象的实例化
                if (object == null) {
                    object = new SingleObject();
                }
            }
        }
        return object;
    }
}

三、问题分析

(一)懒汉模式为什么会有线程安全问题?

【设计模式学习1】什么是单例模式?单例模式的几种实现。_第1张图片
如上图所示,多线程情况下,在时刻T,线程A和线程B都判断single为null,从而进入if代码块中都执行了new Single()的操作创建了两个对象,就和我们当初的单例初衷相悖而行。

(二)为什么要进行两次判空,他们的作用分别是什么?加锁的作用是什么?

1、第一次判空目的:为了缩小锁的粒度,避免每次获取实例都需要进行加锁。
2、加锁目的:在首次获取对象实例时,防止并发线程多次创建对象。
3、第二次判空目的:如果对象没有进行实例化,则进行对象实例化操作

(三)volatile关键字修饰对象的作用是什么?

volatile关键字防止指令重排序造成的空指针异常(通过插入特定的内存屏障的方式来禁止指令重排序)

指令重排序是指:JVM在保证最终结果正确的情况下,可以不按照程序编码的顺序执行语句,尽可能提高程序的性能。

创建一个对象,在JVM中会经过三步:

  • (1)为object对象分配内存空间
  • (2)初始化object对象
  • (3)将object指向分配好的内存空间

由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getSingleObject() 后发现 object不为空,因此返回 object,但此时 object还未被初始化,就会出现空指针异常。

你可能感兴趣的:(设计模式,单例模式,java)