zoukankan      html  css  js  c++  java
  • 设计模式之单例模式

    前言

    设计模式是人们在日常工作中总结出来的一些好的设计方式。用于指导人们能够写出优雅(可扩展,好维护)的代码。

    也能让自己心情愉快。

    简介

    单例模式是一种比较简单的模式。定义为确保某一个类只有一个是实例,而且自行实例化并向整个系统提供这个实例。

    例子

    饿汉式:

    public class Singleton {
        private static final Singleton singeleton = new Singleton();
        // 私有对象可限制new多个对象
        private Singleton(){}
        
        public static Singleton getSingeleton(){
            return singeleton;
        }
        
    }
    

    优点:

    1. 线程安全
    2. 实现简单

    缺点:

    1. 不是懒加载,在加载类时就会被初始化。即使该类你没有被使用。
    2. 如果实例依赖参数则无法实现

    懒汉式 线程不安全(不推荐)

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

    优点:

    1. 实现了懒加载,在单线程下在使用时才能正确创建实例

    缺点:

    1. 虽然使用了懒加载,但是有个严重问题。在多个线程并行调用getInstance()时会创建多个实例。这样在这个充满多线程开发的web应用下是很不可取的。所以不推荐这种用法。

    懒汉式 线程安全(双重检验锁模式)

    使用同步块枷锁的方式来保证线程安全,为何双重判断?当有多个线程同时进入第一个if的时候。如果此时未实例化,则会只有一个线程进入同步代码块,其他代码块将会等待,然后进入第二个。如果该线程在获取锁后已经实例化就跳过实例化,所以存在第二重判断空。
    
    public static Singleton getSingleton() {
        if (instance == null) {                       
            synchronized (Singleton.class) {
                if (instance == null) {              
                    instance = new Singleton();
                }
            }
        }
        return instance ;
    }
    

    以上代码看着很完美,但是它存在一个问题。instance = new Singleton();就是这个

    他并非一个原子操作。这句在jvm中做了三件事情

    1. 分配内存
    2. 调用构造函数初始化成员变量
    3. 分配内存空间(instance !=null)

    由于jvm在编译时存在指令重排序的优化,也就是第二步和第三步有可能被交换。这样就会出现先分配空间在初始化。这时如果有线程到了第一个if就会错误的得到instanc!=null的并没有初始化的实例。如果使用没有初始化的实例则会报错。

    解决方案为添加volatile关键字来防止指令的重排

    优化后的代码:

    public class Singleton {
        private volatile static Singleton instance; //声明成 volatile
        private Singleton (){}
    
        public static Singleton getSingleton() {
            if (instance == null) {                         
                synchronized (Singleton.class) {
                    if (instance == null) {       
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
       
    }
    

    优点:

    1. 实现了懒加载
    2. 线程安全

    缺点:

    1. 代码较复杂而且还隐含jvm问题,不容易理解,一不小心容易犯错
    2. java5以前的版本volatile有缺陷无法避免重排序
    3. 存在同步代码块,性能上可能不如其他方式

    懒汉式,静态内部类(懒汉式加载推荐)

    《Effective Java》上推荐

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

    优点:

    1. 懒加载
    2. 线程安全
    3. 读取实例不同步,性能比双重校验好

    缺点:

    1. 基本无缺点,真要说缺点就是不出名

    小结

    就一般而言,直接使用饿汉式即可,如果要求使用懒加载推荐使用静态内部类。

    为何要使用单列模式

    优点:

    1. 单例模式在内存中,只有一个实例,减少内存开支。
    2. 当一个对象的产生需要比较多资源时,如读取配置可以通过启动时实现一个单例对象来解决
    3. 避免对资源的多重占用,避免对一个资源文件的同时写操作

    缺点:

    1. 没有接口,扩展难
    2. 对测试不理
    3. 与单一职责原则冲突

    使用场景

    1. 要求生成巍译序列号的环境
    2. 整个项目需要一个共享访问点或共享数据,如web页面计数器,使用单例可以保持计数器的值
    3. 创建一个对象需要消耗资源过多,如访问IO和数据库
  • 相关阅读:
    C++
    Qt简介
    C语言
    C/C++
    swagger2 Illegal DefaultValue null for parameter type integer
    maven包引入问题ClassNotFoundException: org.elasticsearch.client.Cancellable
    mysql自定义排序
    使用node创建服务器 运行vue打包的文件
    rsync实现服务器之间同步目录文件
    将jar包发布到maven的中央仓库细节整理
  • 原文地址:https://www.cnblogs.com/willvi624/p/10306396.html
Copyright © 2011-2022 走看看