美文网首页
创建型之单例模式

创建型之单例模式

作者: FisherTige_f2ef | 来源:发表于2021-01-27 18:53 被阅读0次

    什么是单例模式

    是指应用程序全局只能创建唯一一个实例的构造模式。一般的做法是:私有化构造方法(只能由自己创建),提供一个只能创建唯一实例的方法给别人调用

    单例模式的类型

    1、饿汉模式:线程严格安全,但不属于懒加载

    public class Singleton {

        private static Singleton instance = new Singleton(); 

        private Singleton (){} 

        public static Singleton getInstance() { 

        return instance; 

        }  }

    还有两个多此一举的,所谓的饿汉模式。其实就使用上面那种就可以了(可以忽略掉)

    饿汉静态内部类模式(多此一举,不如直接使用简单饿汉模式):线程严格安全,不属于懒加载

    public class Singleton {

        private static class SingletonHolder { 

        private static final Singleton INSTANCE = new Singleton(); 

        } 

        private Singleton (){} 

        public static final Singleton getInstance() { 

        return SingletonHolder.INSTANCE; 

        }  }

    饿汉枚举模式(多此一举,不如直接使用简单饿汉模式):线程严格安全,但不属于懒加载

    public enum Singleton {

        INSTANCE; 

        public void whateverMethod() { 

        }  }

    2、懒汉线程不安全模式:线程不安全,属于懒加载

    public class Singleton {

        private static Singleton instance; 

        private Singleton (){} 

        public static Singleton getInstance() { 

        if (instance == null) { 

            instance = new Singleton(); 

        } 

        return instance; 

        }  }

    3、懒汉线程安全模式:线程不严格安全,属于懒加载

    public class Singleton {

        private static Singleton instance; 

        private Singleton (){} 

        public static synchronized Singleton getInstance() { 

        if (instance == null) { 

            instance = new Singleton(); 

        } 

        return instance; 

        }  }

    4、懒汉volatile双重检验锁模式:线程严格安全,属于懒加载

    public class Singleton {

        private volatile static Singleton singleton; 

        private Singleton (){} 

        public static Singleton getSingleton() { 

        if (singleton == null) { 

            synchronized (Singleton.class) { 

            if (singleton == null) { 

                singleton = new Singleton(); 

            } 

            } 

        } 

        return singleton; 

        }  }

    注意:懒加载是指,当需要调用实例时再检查是否已有实例,没有实例则创建一个实例;非懒加载就是在应用实例创建之初,就创建并加载到内存里的情况

    使用建议:一般在单线程情况下使用第2种方式,如果有多线程需求,一定不允许因为线程原因导致创建两个实例,可以考虑使用第 4 种双检锁方式,或者饿汉模式。

    单例模式的原理

    这里重点讲懒汉volatile双重检验锁模式,其它的比较简单就不细讲了

    1、双重检验锁解决了什么问题

    第一次判断是否为空,是在Synchronized同步代码块外,如果已经创建了singleton对象,就不用进入同步代码块,不用竞争锁,直接返回前面创建的实例,提高效率。

    第二次判断是否为空,是在Synchronized同步代码块内,假若线程A通过了第一次判断,进入了同步代码块,但是还未执行,线程B就进来了(线程B获得CPU时间片),线程B也通过了第一次判断(线程A并未创建实例,所以B通过了第一次判断),准备进入同步代码块,假若这个时候不判断,就会存在这种情况:线程B创建了实例,此时恰好A也获得执行时间片,如果不加以判断,那么线程A也会创建一个实例,就会造成多实例的情况。

    2、volatile解决了什么问题

    volatile的作用有两个:

    一是,所有线程都能及时获得共享内存的最新状态。

    即被volatile修饰的变量,每次读取前必须先从主内存刷新最新的值。每次写入后必须立即同步回主内存当中。

    二是,防止指令重排

    new 创建对象,并不是一个原子操作,它可以抽象为一下几条jvm指令

    memory = allocate(); //1:分配对象的内存空间

    initInstance(memory);//2:初始化对象

    instance = memory;  //3:设置instance指向刚分配的内存地址

    上面操作2依赖于操作1,但是操作3并不依赖于操作2,所以JVM可以以“优化”为目的对它们进行重排序,多线程情况下,导致可能出现误判为空的情况。

    而被volatile修饰的变量,会遵循以下原子操作规则:

    read、load、use动作必须连续出现

    assign、store、write动作必须连续出现

    因此,volatile修饰该单例能在更细的粒度保证数据在多线程的情况里的一致性,被免误判为空的情况。

    ps:Java变量的读写,主要通过以下几种原子操作完成工作内存和主内存的交互

    lock:作用于主内存,把变量标识为线程独占状态。

    unlock:作用于主内存,解除独占状态。

    read:作用主内存,把一个变量的值从主内存传输到线程的工作内存。

    load:作用于工作内存,把read操作传过来的变量值放入工作内存的变量副本中。

    use:作用工作内存,把工作内存当中的一个变量值传给执行引擎。

    assign:作用工作内存,把一个从执行引擎接收到的值赋值给工作内存的变量。

    store:作用于工作内存的变量,把工作内存的一个变量的值传送到主内存中。

    write:作用于主内存的变量,把store操作传来的变量的值放入主内存的变量中

    单例模式的应用场景

    对于一些全局通用的实例,但是创建消耗代价很大,容易被重复创建的场景都可使用,例如:图片加载实例,语音服务实例等。

    相关文章

      网友评论

          本文标题:创建型之单例模式

          本文链接:https://www.haomeiwen.com/subject/erkxiktx.html