zoukankan      html  css  js  c++  java
  • 【Java设计模式】单例模式


    ### 1. 概述
    > 单例模式是确保某一个类中有且只有一个实例。

    ----------
    ### 2. 饿汉式单例
    ``` java
    public class SingletonInstance {
    private static SingletonInstance mInstance = new SingletonInstance();
    // 默认私有构造方法
    private SingletonInstance(){}
    // 静态工厂方法
    public static SingletonInstance getInstance(){
    return mInstance ;
    }
    }
    ```
    在饿汉式单例中,静态变量会在私有构造方法中初始化,这时候唯一的实例就被创建出来了,饿汉式主要使用到的是**空间换时间**的思想,在类还在加载的时候,对象实例便已经创建好了。

    ----------

    ### 3. 懒汉式单例
    ```java
    public class SingletonInstance {
    private static SingletonInstance mInstance = null;
    // 私有默认构造方法
    private SingletonInstance(){}
    // 静态工厂方法
    public static synchronized SingletonInstance getInstance(){
    if(mInstance == null){
    mInstance = new SingletonInstance();
    }
    return mInstance;
    }
    }
    ```
    对于懒汉式单例的处理,使用了``synchronized``参数修饰工厂方法,用来在多线程环境中解决同步问题,懒汉式主要是使用到的是**时间换空间**的思想,在获取实例的时候进行判断,只有在需要的时候才去创建对象,节省内存空间,但是缺点就是实现的方式是线程安全的,这样会降低访问的速度。

    ----------
    ### 4. 双重加锁单例
    ```java
    public class SingletonInstance {
    private volatile static SingletonInstance mInstance = null;
    private SingletonInstance(){}
    public static SingletonInstance getInstance(){
    //先检查实例是否存在,如果不存在才进入下面的同步块
    if(mInstance == null){
    //同步块,线程安全的创建实例
    synchronized (SingletonInstance .class) {
    //再次检查实例是否存在,如果不存在才真正的创建实例
    if(mInstance == null){
    mInstance = new SingletonInstance ();
    }
    }
    }
    return mInstance;
    }
    }
    ```
    双重加锁机制指的是,在每次进入``getInstance``方法先不同步,而是进入方法后,先检查实例是否存在,如果不存在才进行下面的同步块,这属于第一重检查,进入同步块过后再检查实例是否存在,如果不存在,就在同步的情况下创建一个新的实例,这属于第二重检查。这样便只需要同步一次,并减少了在多次同步情况下进行判断浪费的时间。
    这种实现方式会使用到**volatile**关键字,意思是被**volatile**修饰的变量的值,不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而保证线程正确的处理该变量。
    > **volatile**关键字在**JDK5**之前的版本中加锁失败,注意之。而且**volatile**关键字会屏蔽掉虚拟机中的一些必要的代码优化,因此虽然能实现双重检查加锁机制的单例,但并不建议大量采用。

    那有什么方案可以既能达到延迟加载,又能实现线程安全的目的呢?

    -----
    ### 5. Lazy Initialization Holder Class模式
    ```java
    public class SingletonInstance {

    private SingletonInstance(){}
    /**
    * 类级的内部类的实例与外部类的实例没有绑定关系,而且只有被调用到时才会装载
    */
    private static class SingletonHolder{
    /**
    * 静态初始化,由JVM来保证线程安全
    */
    private static SingletonInstance mInstance = new SingletonInstance();
    }
    public static SingletonInstance getInstance(){
    return SingletonHolder.mInstance;
    }
    }
    ```
    如果只是想简单的实现线程安全的单例,可以使用之前的**饿汉式**方式。但是缺点就是会在类装载的时候初始化对象,造成空间的浪费。
    那么只要解决了类加载时自动初始化对象的问题,便可以解决问题。因此可以采用类级内部类的方式去实现,这样的话,只有在需要的时候才会创建对象实例,也达到了延迟加载和线程安全的目的。
    从实现过程来看,但调用`getInstance`方法时,它会读取`SingletonHolder.mInstance`,从而初始化,在这个类被装载的时候,也会初始化静态成员,而由于静态域的特性,只会初始化一次,并且由JVM来保证线程安全。
    >- 什么是类级内部类?
    被`static`修饰的成员内部类才是类级内部类,如果没有被`static`修饰则被称为对象内部类,而且类级内部类与外部类对象不存在依赖关系,只有在第一次使用的时候才会被调用。
    >- 多线程默认同步锁知识?
    在多线程开发中,我们主要使用`synchronized`来对互斥锁进行同步控制,但是某些情况下JVM已经为我们进行了同步控制了,主要有:
    1. 静态初始化方法初始化数据时;
    2. 访问`final`字段时;
    3. 在创建线程之前创建对象时;
    4. 线程可以看见要处理的对象时;

    ----------
    ### 6. 枚举式单例
    ```java
    public enum SingletonInstace{
    // 定义一个枚举元素,它代表了一个实例
    mInstance;
    // 单例的操作
    public void singletonOperation(){

    }
    }
    ```
    使用枚举的方式既使得代码简洁,而且也由JVM来保证序列化机制,防止多次实例化,是最佳的实现单例的方式。

  • 相关阅读:
    Oracle-通过创建索引加快SQL执行效率
    Oracle-DG,MRP进程无法正常应用问题处理,重启大法好
    Oracle-DG,12c pdb创建测试
    Oracle-DG,疑问主库添加日志后,备库未操作主库日志比备库日志数量多,有什么影响?
    Oracle-DG疑问,什么情况下主库会发出一个会话连接备库
    Oracle-DG 主库将log_archive_dest_state_2远程归档线程参数设置为defer,为什么dg还是处于实时同步状态?
    Oracle-rm误删除数据文件,如何强制删除文件启动db
    Oracle-buffer cache过小导致SQL执行时间长
    win10下完全卸载-重装MySQL
    VSCode配置详细教程
  • 原文地址:https://www.cnblogs.com/dennisac/p/4734868.html
Copyright © 2011-2022 走看看