zoukankan      html  css  js  c++  java
  • 单例模式 代码以及祥解

    单例模式的

    本质:

      控制实例数目

    定义:

      保证一个类仅有一个实例,并提供一个它的全局访问点。

      单例模式有两种实现方式:饿汉模式和懒汉模式。

    懒汉式实现代码

    public class Singleton{

      private static Singleton uniqueInstance = null;

      private Singleton(){}

      private static synchronized Singleton getInstance(){

        if(uniqueInstance == null){

          uniqueInstance = new Singleton();

        }  

        return uniqueInstance;

      }

    }

    饿汉式实现代码:

    public class Singleton{

      private static Singleton uniqueInstance = new Singleton();

      private Singleton(){}

      private static synchronized Singleton getInstance(){ 

        return uniqueInstance;

      }

    }

    单例模式实现

      1.私有化构造函数;

      2.提供获取实例的方法;

      3.把获取实例的方法变为静态;

      4.定义存储实例的属性;

      5.把这个属性也定义为静态的;

      6.实现控制实例的实现(懒汉式);

      饿汉式实现方式体现了延迟加载的思想(简单说就是等到用这个资源的时候才加载,节约了资源);同时还体现了缓存的思想,当一个资源被频繁利用的时候,如果每次操作都从数据库或者硬盘上获取会很慢,所有有一种方式是存到内存中,要用到的时候,现在内存里面找,有就直接使用,从而节省大量时间。缓存是一种典型的利用空间换时间的方案

    时间和空间

      比较两种实现方式:懒汉式是典型的时间换空间,也就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。当然,如果一直没有人使用的话,就一直不被创建,则节约内存空间。

                饿汉式是典型的空间换时间,当类装载时就会被创建类实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断了,节约了运行时间。

    线程安全

      从线程安全的角度来看,不加同步的懒汉式单例模式是线程不安全的,比如,有两个线程,一个A线程,一个B线程,它们同时调用getInstance()方法,那就可能导致并发问题,实例如下:

      

       

      饿汉式是线程安全的,因为虚拟机保证只会装载一次,在装载类的时候是不会发生并发的。

    实现懒汉式的线程安全

      1.加synchronized,降低了访问效率,而且每次都还需要判断。

       2.“双重检测加锁”的方式实现,既使线程安全,又使性能不受太大影响,实现:

        使用到volatile关键字:被volatile修饰的变量的值,将不被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。

        其实使用volatile和不使用都是线程安全的(主要是synchronized代码块保证线程安全),主要作用不是在线程安全,而是禁止在多线程的情况下代码重排序(new Singleton()时可能会出现重排序)

        

        根据上图步骤解析(个人理解):例如有两个线程,A线程运行了伪代码的1,3步骤还没有运行到2,此时B线程进入方法2步骤,它会对变量进行取值操作,如果没有volatile的话它会直接执行取值,此时值为null,B线程就会往下执行,到synchronized代码块进行排队;如果有volatile,变量就不会重排序,取值操作会在执行完伪代码2步骤后执行,此时取值操作就会取到对象,线程直接会输出对象。还有一种理解是, CPU缓存中的volatile字段被一个线程修改后,其他CPU缓存中的线程在读 本地CPU缓存的 volatile字段时,就必须读取更新过的字段,所以B线程操作变量必须等A线程更新完才进行。

    (频繁更改、改变或写入volatile字段 有可能导致性能问题)

         实现代码:

          public class Singleton{  

            private volatile static Singleton uniqueInstance = null;

            private Singleton(){}

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

          }

     更好单例实现模式

      解决方案(Lazy initialization holder class模式)

        采用静态初始化器的方式,它可以由JVM来保证线程的安全性。

        实现代码:

          public class Singleton{  

            private Singleton(){}

               private static class SingletonHolder{

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

          }

      静态内部类和它外部类没有直接联系,只有在第一次调用getInstance()方法,第一次读取SingletonHolder.instance时,SingletonHolder类得到初始化,而这个类在装载并被初始化的时候才会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,所有只会在虚拟机装载类的时候初始化一次,并由虚拟机保证它的线程安全性。

      优势:getInstance方法并没有被同步,并只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。

    用枚举来实现单例(最佳方法)

      用枚举实现单例非常简单,只需要编写一个包含单个元素的枚举类型即可。

      实现代码:

      public enum Singleton{

        /**定义一个枚举的元素,他就代表了Singleton的一个实例**/

        uniqueInstance;

        /**单例也可以有自己的操作方法**/

        public void singletonOperation(){

        //功能处理

        }

      }

    使用枚举实现单实例控制会更简洁,而且无偿的提供了序列化机制,并由JVM从根本上提供了保障,绝对防止多次实例化,是更简洁,高效,安全的实现方式。

  • 相关阅读:
    C标准库函数实现
    每天一句话
    移植数码相框到arm开发板上
    数据管理
    perl 分割文件路径和文件名
    恶补英语 拿 The C programming language 练功
    英语学习 chapter1
    uboot 烧写过程
    MVC,去掉字符串中的html代码
    排序之希尔排序
  • 原文地址:https://www.cnblogs.com/johnnyC/p/9432133.html
Copyright © 2011-2022 走看看