• java23种设计模式之二: 单例设计模式(6种写法)


    定义:

      指一个类只有一个实例,且该类能自行创建这个实例的一种模式.

    特点:

      单例类只有一个实例对象;

      该单例对象必须由单例类自行创建;

      单例类对外提供一个访问该单例的全局访问点;

    应用场景:

      多线程中的线程池、数据库的连接池、网站的计数器、Web 应用的配置对象、应用程序中的对话框、 系统中的缓存等常常被设计成单例

    模式结构:

      1.单例类:包含一个实例且能自行创建这个实例的类

      2.访问类:使用单例的类

    实现:

      单例模式的实现通常又分为了6种实现方式

      1.饿汉式

      2.懒汉式(unsafe)

      3.懒汉式(secruity)

      4.懒汉式(Double check)

      5.懒汉式(volatile security)

      6.静态内部类(inner_static)

    总述:如果要写一个单例模式,要私有构造函数,对外提供唯一对象实例。

    本文共有6种写法,仅供参考(全篇阅读约10分钟)

    1.饿汉式

       优点: 在多线程情况下,该方法创建的单例是线程安全的(立即加载

       缺点: 由于instance 是由静态修饰的,所以在加载类之前就会将instance 加载到方法区中,如果长时间不用,这样会长时间占用内存。

    public class HungrySingleton {
        
        private static HungrySingleton instance = new HungrySingleton();
        
        private HungrySingleton() {};
        
        public HungrySingleton getHungrySingleton() {
            return instance;
        }
    }

    2.懒汉式(Unsafe)

      优点:只有在使用时才会被创建,不会在初始化时消耗大量内存

      缺点:线程是不安全的。当被多个线程调用时,线程是不安全,需要在获取实例方法添加synchronized

    public class LazySingleton {
        
        private static LazySingleton instance ;
    private LazySingleton() {} public LazySingleton getLayzSingleton() { if(instance == null) instance = new LazySingleton(); // return instance; return LazySingleton.instance; ////此种写法更直观,可以看出是某个类的实例变量 } }

    3.懒汉式(security)

      优点:只有在使用时才会被创建,不会在初始化时消耗内存,同时又确保了线程是安全的

      缺点:虽然保证线程是安全,由于加了同步锁会在并发访问时影响性能

    public class LazySingletonSecurity {
        
        private static LazySingletonSecurity instance;
        
        private LazySingletonSecurity() {}
        
        public synchronized LazySingletonSecurity getInstance() {
            if(instance == null)
                instance = new LazySingletonSecurity();
            return instance;
    //        return LazySingletonSecurity.instance;
        }
    }

    4.懒汉式(double check)

         在示例3中,在getInstance()获取实例的方法是同步的,其目的是为了防止多个线程在获取实例时并发的创建多个对象,但是这样影响了访问的效率,既然是为了防止多线程并发访问,那我们可以把同步方法改成同步代码块,在创建instance 实例处加上同步代码块,当线程1访问instance == null;进来拿到同步锁,将实例创建完成才释放锁,当线程2访问时,instance 已经不等于null 直接返回了实例。

      优点:在使用时都会被创建,访问效率高、线程安全

      缺点:可能会实现空指针情况

    public class LazySingletonDoubleCheck {
        
        private static LazySingletonDoubleCheck instance;
        private LazySingletonDoubleCheck() {}
        
        /**
         *  synchronized处:当线程1执行完创建实例释放锁后,线程2执行到此处代码时,得到实例不等于null,于是
         *  返回拿到了返还的实例,可能这样解释你觉得怎么会出现这种情况,但这种情况确实会出现,是因为java
         *  的happens-before规则导致的,在代码编译后,编译器和处理器会进行优化处理,但在在堆内存创建
         *  对象后直接返回了,可能还没有将对象的一些属性初始化完成,而线程2得到的实例可能会出现空指针的情况。
         */
        public LazySingletonDoubleCheck getInstance() {
            if(instance == null) {
                synchronized (LazySingletonDoubleCheck.class) {
                    if (instance == null) 
                        instance = new LazySingletonDoubleCheck();
                }
            }
            return instance;
        }
    }

    5.懒汉式(volatile security)

      优点:在使用时都会被创建,访问效率高、线程安全

      缺点:如果对关键字性能不了解,这种写法可能不太会被接受

        volatile:虽然不能保证原子性,但保证内存的可见性,即多个线程看到的数据是同一份,它在加载读的时候,会保证所有写的操作完成之后,才会去读,即保证了在创建对象的时候会保证对象完全初始化创建完成。

    public class LazySingletonVolatileSecurity {
        
        private static volatile LazySingletonVolatileSecurity instance;
        
        private LazySingletonVolatileSecurity() {};
        
        public LazySingletonVolatileSecurity getInstance() {
            if (instance == null) {
                synchronized (LazySingletonVolatileSecurity.class) {
                    if(instance == null)
                        instance = new LazySingletonVolatileSecurity();
                }
            }
            
            return instance;
        }
    }

    6.静态内部类(inner_static)

      优点:

        1.static 修饰的类,只有使用到时才会进行加载,而且只加载1

        2.这种方式是通才内部类调用外部类构造函数,同时提供1种对外访问的方法来实现的

      缺点:需要对jvm加载class的时机掌握的比较清楚

    public class LazySingletonInnerStatic {
        
        private LazySingletonInnerStatic() {}
        
        private static class InnerClass{
            private static final LazySingletonInnerStatic instace = new LazySingletonInnerStatic();
        }
        
        public LazySingletonInnerStatic getInstance() {
            return LazySingletonInnerStatic.InnerClass.instace;
        }
    }
  • 相关阅读:
    Oracle 提示 用户在线,无法删除的清理方法
    拉格朗日乘子法和KKT条件
    主题模型及其在文本情感分析中的应用
    spring-data-elasticsearch整合elasticsearch
    自然数的K次幂的数列求和
    SVM 简要推导过程
    机器学习中导数最优化方法(基础篇)
    漫谈:机器学习中距离和相似性度量方法
    A geometric interpretation of the covariance matrix
    数据挖掘算法之协同过滤算法
  • 原文地址:https://www.cnblogs.com/MrRightZhao/p/10463313.html
Copyright © 2020-2023  润新知