zoukankan      html  css  js  c++  java
  • 三、单例模式

    单例模式

    前言

    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

    这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

    注意:

    • 1、单例类只能有一个实例。
    • 2、单例类必须自己创建自己的唯一实例。
    • 3、单例类必须给所有其他对象提供这一实例。

    单例模式的几种实现方式

    【1】懒汉式(线程不安全)

    public class Singleton {  
        private static Singleton instance;  
        private Singleton (){}  
    
        public static Singleton getInstance() {  
            if (instance == null) {  
                instance = new Singleton();  
            }  
            return instance;  
        }  
    } 
    

    这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
    这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。

    【2】懒汉式(线程安全)

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

    这种方式的好处是写起来简单,且绝对线程安全;坏处是并发性能极差,事实上完全退化到了串行。单例只需要初始化一次,但就算初始化以后,synchronized的锁也无法避开,从而getInstance()完全变成了串行操作。性能不敏感的场景建议使用

    【3】饿汉式

    public class Singleton {  
        private static Singleton instance = new Singleton();  
        private Singleton (){}  
        public static Singleton getInstance() {  
            return instance;  
        }  
    }  
    

    这种方式比较常用,但容易产生垃圾对象,即类加载时初始化单例,以后访问时直接返回即可。

    它基于类加载机制避免了多线程的同步问题,但是没有达到懒加载的效果

    【4】双重校验锁(DCL)

    public class Singleton {
        private static Singleton instance;
        
        public int f1 = 1;   // 触发部分初始化问题
        public int f2 = 2;
        	
        private Singleton(){}
    	
        public static Singleton getInstance() {
            if (instance == null) { // 当instance不为null时,可能指向一个“被部分初始化的对象”
                synchronized (Singleton.class) {
                    if ( instance == null ) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    

    这种方式看起来似乎已经达到了理想的效果:懒加载+线程安全。但是DCL仍然是线程不安全的,由于指令重排序,会遇到”部分初始化问题”。

    问题出在这行简单的赋值语句:

    instance = new Singleton();
    

    它并不是一个原子操作。事实上,它可以”抽象“为下面几条JVM指令:

    memory = allocate();	//1:分配对象的内存空间
    initInstance(memory);	//2:初始化对象(对f1、f2初始化)
    instance = memory;		//3:设置instance指向刚分配的内存地址
    

    上面操作2依赖于操作1,但是操作3并不依赖于操作2,所以JVM可以以“优化”为目的对它们进行重排序,经过重排序后如下:

    memory = allocate();	//1:分配对象的内存空间
    instance = memory;		//3:设置instance指向刚分配的内存地址(此时对象还未初始化)
    ctorInstance(memory);	//2:初始化对象
    

    可以看到指令重排之后,操作 3 排在了操作 2 之前,即引用instance指向内存memory时,这段崭新的内存还没有初始化——即,引用instance指向了一个”被部分初始化的对象”。此时,如果另一个线程调用getInstance方法,由于instance已经指向了一块内存空间,从而if条件判为false,方法返回instance引用,用户得到了没有完成初始化的“半个”单例。
    解决这个该问题,只需要将instance声明为volatile变量

    【5】登记式/静态内部类

    public class Singleton {  
        private static class SingletonHolder {  
            private static final Singleton INSTANCE = new Singleton();  
        }  
        private Singleton (){}  
        public static final Singleton getInstance() {  
            return SingletonHolder.INSTANCE;  
        }  
    }  
    

    这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
    这种方式同样利用了类加载机制来保证初始化 instance 时只有一个线程,它跟第饿汉式种方式不同的是:饿汉式方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。

    【6】枚举

    public enum Singleton {  
        INSTANCE;  
        public void whateverMethod() {  
        }  
    }  
    
    

    这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。

  • 相关阅读:
    编码和字符集
    【机器学习】模型泛化
    asp.net GridView控件的列属性
    asp.net截取指定长度的字符串内容
    asp.net 对数据库表增加,删除,编辑更新修改
    asp.net 链接数据库ADO.NET
    常用正则表达式 验证电子邮件网址邮政编码等
    ASP.NET获取文件的相关知识
    C#获取picturebox图片路径
    C# dataGridView根据数据调整列宽
  • 原文地址:https://www.cnblogs.com/lee0527/p/11875485.html
Copyright © 2011-2022 走看看