2021-02-21 单例模式

1.什么是单例模式

    单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。

2.单例模式的类型

单例模式有两种类型:

    饿汉式:在真正需要使用对象时才去创建该单例模式;

    懒汉式:在类加载的时候就已经创建好对象,等待被使用;

饿汉式:

public class Singleton{

    private static Singleton singleton;

    public static Singleton getInstance(){

        if(singleton == null){

            synchronized(Singleton.class){

                If(singleton ==null){

                    singleton = new Singleton;

                }

            }

        }

    return singleton;

    }

}

懒汉式:

public class Singleton{

    private static final  Singleton singleton = new Singleton();

    public static Singleton getInstance(){

        return singleton ;

    }

}

3.使用volatile防止指令重排

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

(1)为singleton对象分配内存空间

(2)初始化singleton对象

(3)将singleton指向分配好的内存空间

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

在这三步中,第2、3步有可能会发生指令重排现象,创建对象的顺序变为1-3-2,会导致多个线程获取对象时,有可能线程A创建对象的过程中,执行了1、3步骤,线程B判断singleton已经不为空,获取到未初始化的singleton对象,就会报NPE异常。文字较为晦涩,可以看流程图:

使用volatile关键字可以防止指令重排序,其原理较为复杂,这不打算展开,可以这样理解:使用volatile关键字修饰的变量,可以保证其指令执行的顺序与程序指明的顺序一致,不会发生顺序变换,这样在多线程环境下就不会发生NPE异常了。

volatile还有第二个作用:使用volatile关键字修饰的变量,可以保证其内存可见性,即每一时刻线程读取到该变量的值都是内存中最新的那个值,线程每次操作该变量都需要先读取该变量。

最终的代码如下所示:

public class Singleton {

    private static volatile Singleton singleton;

    private Singleton(){}

    public static Singleton getInstance() {

        if (singleton == null) {  // 线程A和线程B同时看到singleton = null,如果不为null,则直接返回singleton            

            synchronized(Singleton.class) { // 线程A或线程B获得该锁进行初始化                

                    if (singleton == null) { // 其中一个线程进入该分支,另外一个线程则不会进入该分支

                        singleton = new Singleton();

                }

            }

        }

        return singleton;

    }

    }


4.总结

(1)单例模式有两种:懒汉式、饿汉式

(2)懒汉式:在需要用到对象时才实例化对象,正确的实现方式是:Double Check + Lock,解决了并发安全和性能低下问题

(3)饿汉式:在类加载时已经创建好该单例对象,在获取单例对象时直接返回对象即可,不会存在并发安全和性能问题。

(4)在开发中如果对内存要求非常高,那么使用懒汉式写法,可以在特定时候才创建该对象;

(5)如果对内存要求不高使用饿汉式写法,因为简单不易出错,且没有任何并发安全和性能问题

(6)为了防止多线程环境下,因为指令重排序导致变量报NPE,需要在单例对象上添加volatile关键字防止指令重排序

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容