• 单例模式


    所有单例模式都有一个共性,那就是这个类没有自己的状态。也就是说无论这个类有多少个实例,都是一样的;然后除此者外更重要的是,这个类如果有两个或两个以上的实例的话程序会产生错误。

    基于上述原因,非线程安全的实现方式,在此不再讨论。下面讨论的都是线程安全的一些实现方式和存在的问题。

    双重加锁模式

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

    双重加锁模式相对于普通的单例和加锁模式而言,从性能和线程安全上来说都有很大的提升和保障。然而双重加锁模式也存在一些隐蔽不易被发现的问题。首先我们要明白在JVM创建新的对象时,主要要经过三个步骤。

      • 分配内存
      • 初始化构造器
      • 将对象指向分配的内存地址

    这样的顺序在双重加锁模式下是么有问题的,对象在初始化完成之后再把内存地址指向对象。但是现代的JVM会针对字节码进行调优,这样的话就有可能导致2和3的顺序是相反的,一旦出现这样的情况问题就来了。

    所以更加理想的方案是利用静态内部类的方式来创建,因为静态属性由JVM确保第一次初始化时创建,因此也不用担心并发的问题出现。当初始化进行到一半的时候,别的线程是无法使用的,因为JVM会帮我们强行同步这个过程。另外由于静态变量只初始化一次,所以singleton仍然是单例的。

    静态内部类的方式

    public class Singleton{
    
      private Singleton(){}
    
      public static Singleton getInstance(){
        return InnerClassSingleton.singleton;
      }
    
      private class InnerClassSingleton{
        protected static Singleton singleton = new Singleton();
      }
    }
    

    然而,虽然静态内部类模式可以很好地避免并发创建出多个实例的问题,但这种方式仍然有其存在的隐患。

      • 一旦一个实例被持久化后重新生成的实例仍然有可能是不唯一的。
      • 由于java提供了反射机制,通过反射机制仍然有可能生成多个实例。

    单例最优方案,枚举的方式

    枚举实现单例的优势

    • 自由序列化;
    • 保证只有一个实例(即使使用反射机制也无法多次实例化一个枚举量);
    • 线程安全;
    public enum Singleton {
        INSTANCE;
    
        private Singleton(){}
    }
    

    参考文档:

  • 相关阅读:
    Shell Sort
    Insertion Sort
    Notations
    Nakamori Akina
    QuickSort
    Onedrive File Open Problem
    JSON Introduction
    System Call
    进程软中断通信
    Bubble Sort
  • 原文地址:https://www.cnblogs.com/rwxwsblog/p/6154976.html
Copyright © 2020-2023  润新知