设计模式-单例模式(代码分析+例子)

设计模式-单例模式

文章目录

  • 设计模式-单例模式
    • 一 单例模式介绍
    • 二 实现单例模式主要关键点
    • 三 单例模式实现方式
      • 1. 懒汉模式(个人理解:懒,就是调用时候再new)
      • 2. 饿汉模式(个人理解:饿,new好,直接获取)
    • 四 Double Check Lock-DCL(双重锁定机制)实现单例,不赞成使用
      • 点一 两次判空
      • 点二 DCL乱序处理之volatile
      • 点三 DCL优缺点
    • 五 静态内部类单例模式(推荐使用)
    • 六 单例总结
      • 优点
      • 缺点
    • 尾言
    • 本文单例Demo源码

一 单例模式介绍

单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。

本文单例Demo源码

二 实现单例模式主要关键点

  1. 构造函数不对外开放,一般为Private
  2. 通过一个静态方法或者枚举返回单例类对象
  3. 确保单例类的对象有且只有一个,尤其是在多线程环境下
  4. 确保单例类对象在反系列化时不会重新构建对象

三 单例模式实现方式

1. 懒汉模式(个人理解:懒,就是调用时候再new)

/**
 * @ 创建:   kx
 * @ 时间:    2018/12/3
 * @ 描述:    懒汉模式
 */

public class Singleton {
    
    private static Singleton instance;
    
    private Singleton(){}
    
    public static synchronized  Singleton getInstance(){
        if(instance == null){
           //懒汉模式(个人理解:懒,就是调用时候再new) 
            instance = new Singleton();
        }
        return instance;
    }
}

2. 饿汉模式(个人理解:饿,new好,直接获取)

/**
 * @ 创建:   kx
 * @ 时间:    2018/12/3
 * @ 描述:    饿汉模式
 */

public class Singleton {
   // (个人理解:饿,new好,直接获取)
    private static Singleton instance = new Singleton();
    
    private Singleton(){}
    
    public static synchronized  Singleton getInstance(){
        return instance;
    }
}

四 Double Check Lock-DCL(双重锁定机制)实现单例,不赞成使用

DCL方式实现单例模式的有点是既能在需要时菜初始化单例,又能保证线程安全,且单例对象初始化后调用getInstance不进行同步锁

package com.kx.singleinstance;

/**
 * @ 创建:   kx
 * @ 时间:    2018/12/3
 * @ 描述:
 */

public class Singleton {

    private static Singleton sSingleton = null;

    private Singleton() {
    }

    public void sysoHello() {
        System.out.println("hello");
    }

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

点一 两次判空

getInstance()方法上,可以看到getInstance方法中对sSingleton 进行了两次判空

  1. 为了避免不必要的同步
  2. 为了在null时创建实例

点二 DCL乱序处理之volatile

在 sSingleton = new Singleton();执行时,它并不是一个原子操作,这句代码最终会被编译成多条汇编指令

  1. 给Singleton的实例分配内存
  2. 调用Singleton()的构造函数,并初始化成员变量
  3. 将sSingleton对象指向分配的内存空间(这里有点像C的指针的指针),sSingleton就不再null

但是,在JDK1.5前Java编译器预习处理器乱序执行,上面的2和3顺序是无法保证的.执行顺序可能1-2-3或者1-3-2.如果是后者,可能出错值就是DCL失效问题,而且这种难以跟踪和重现的错误可能隐藏很久.

JDK1.5后,SUN官方调整JVM,具体化了volatile关键字, private volatile static Singleton sSingleton = null;即可保证sSingleton 每次都是从主内存中读取,就可以使用DCL的写法来完成单例模式.当然volatile也会影响到性能

点三 DCL优缺点

优点: 资源利用率高,第一次执行getInstance时单例对象才会被实例化,效率高
缺点:第一次加载时反应稍慢,高并发环境下也有一定缺陷

五 静态内部类单例模式(推荐使用)

DCL虽然在一定程度上解决了资源消耗,多余的同步,线程安全等问题,但是,它还是在某些情况下出现失效的问题.这个问题被称为双重检查锁定失效,在<>中谈及这个问题,并指出这种"优化"是丑陋的,不赞成使用.而 建议使用如下代码:

package com.kx.singleinstance;

/**
 * @ 创建:   kx
 * @ 时间:    2018/12/3
 * @ 描述:
 */

public class SingletonStatic {

    private SingletonStatic() {
    }

    public static SingletonStatic getInstance() {
        return SingletonHolder.sInstance;
    }

    /**
     * 静态内部类
     */
    private static class SingletonHolder{
        public static final SingletonStatic sInstance = new SingletonStatic();
    }
}

考虑反射:由于在调用 SingletonHolder.instance 的时候,才会对单例进行初始化,而且通过反射,是不能从外部类获取内部类的属性的。所以这种形式,很好的避免了反射入侵。

考虑多线程:由于静态内部类的特性,只有在其被第一次引用的时候才会被加载,所以可以保证其线程安全性。

优势:兼顾了懒汉模式的内存优化(使用时才初始化)以及饿汉模式的安全性(不会被反射入侵)。
劣势 : 需要两个类去做到这一点,虽然不会创建静态内部类的对象,但是其 Class 对象还是会被创建,而且是属于永久带的对象。

六 单例总结

优点

  1. 单例模式在内存中只有一个实例,减少了内存,系统的性能开支,特别是一个对象需要频繁地创建,销毁时,而且创建货销毁时性能又无法优化,单例模式的优势就非常明显
  2. 单例模式可以避免对资源的多重占用,如文件的读写操作
  3. 单例模式可以在系统设置全局的访问点,优化和共享资源访问,如 AppApplication中 静态bean存储临时内存数据供全局访问

缺点

  1. 单例模式一般没有接口,拓展困难,只能修改代码途径实现
  2. 单例对象持有Context,容易引发内存泄露,这里最好使用Application Context

尾言

本文如有错误或不当之处,欢迎读者留言斧正,互相交流学习,博主不胜感激.联系邮箱[email protected]

本文单例Demo源码

本文单例Demo源码

你可能感兴趣的:(Java,设计模式)