zoukankan      html  css  js  c++  java
  • 浅谈单例设计模式的几种实现方式

    在设计模式中,最常谈及的就是单例设计模式。

    百度百科对于单例设计模式的设计动机是这么阐述的:

    上述是一个广义的概念,那么在具体开发中单例带来了什么呢?

    在java语言中,单例带来了两大好处:

    1.对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级的对象而言,是非常可观的一笔系统开销。

    2.由于new操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻GC压力,缩短GC停顿时间。

    所以对于系统的关键组件和被频繁操作的对象,使用单例模式便可以有效地改善系统性能。

    单例的参与者非常简单,只有单例类和使用者两个;

    下面介绍单例设计模式在java代码的具体实现:

    单例模式的核心在于通过一个接口返回唯一的对象实例,一个简单的单例实现如下:

     1 public class Singleton {
     2     private  Singleton() {
     3         System.out.println("Singleton  is  create"); // 创建单例的过程可能会比较慢
     4     }
     5 
     6     private static Singleton instance = new Singleton();
     7 
     8     private static Singleton getInstance() {
     9         return instance;
    10     }
    11 }

    注意代码的蓝色部分,首先单例类必须要有一个private访问级别的构造函数,只有这样,才能确保单例不会在系统中的其他代码内被实例化,这点是相当重要的;其次,instance成员和getInstance()方法必须是static的。

    这种单例的实现方式非常简单,而且十分可靠,它唯一的不足仅是无法对instance实例做延迟加载,假如单例的创建过程很慢,而由于instance成员变量是static定义的,因此在JVM加载单例类时,单例对象就会被建立,如果此时,这个单例类在系统中还扮演其他角色,那么在任何使用这个单例类的地方都会初始化这个单例变量,根本就不会管是否被用到。比如单例String工厂,用于创建一些字符串(该类既用于创建单例Singleton,又用于创建String对象)。

    public class Singleton {
    	private Singleton() {
    		System.out.println("Singleton  is  create"); // 创建单例的过程可能会比较慢
    	}
    
    	private static Singleton instance = new Singleton();
    
    	private static Singleton getInstance() {
    		return instance;
    	}
    	
    	public static void CreateString(){       //这是模拟单例类扮演其他角色
    		System.out.println("createString in Singleton");
    	}
    }
    

      上面代码的意思就是,假如单例类里面有getInstance以外的其它静态方法,跟单例没啥关系的方法,如果使用了Singleton.CreateString()这种调用,就会自动创建Singleton这个类实例(虽然private构造已经防止了你人为去new这个单例),这是开发人员不愿看到的,我运行下面代码(此代码调用了上面代码的createString()方法)进行展示:

     那么在这个单例基础上进行延迟加载改良:

     1 public class Singleton {
     2     private Singleton() {
     3         System.out.println("LazySingleton  is  create"); // 创建单例的过程可能会比较慢
     4     }
     5 
     6     private static Singleton instance = null;
     7 
     8     public static synchronized Singleton getInstance() {
     9         if (instance == null)
    10             instance = new Singleton();
    11         return instance;
    12     }
    13 
    14 }

    此时再调用同样的测试方法:

    解决了这个问题。

    显而易见,这次改良主要是从JVM加载类的原理上进行改良的,上面的代码在初始化类的时候给instance赋予null,确保系统启动的时候没有额外的负载,其次,在getInstance方法中加入判断单例是否存在,不存在才new。但是getInstance()方法必须是同步的,

    否则多线程环境下,可能线程1正在创建单例时,线程2判断单例instance为空,就会创建多个实例。

    但是上面的写法,存在性能问题,在多线程下,它的耗时远远大于第一种单例。以下代码就说明了此问题:

    1     @Override
    2         public void run(){
    3             for (int i = 0; i < 100000; i++) 
    4                 Singleton.getInstance();
    5                 System.out.println("spend:" (System.currentTimeMillis()-begintime));
    6             
    7         }

    开启五个线程同时完成以上代码的运行,使用第一种单例耗时0ms,而使用第二种单例耗时390ms,性能至少相差两个数量级。

    为了线程安全使用了同步关键字反而降低了系统性能,为了解决这个问题,继续改进:

     1 public class Singleton {
     2     private Singleton() {
     3         System.out.println("LazySingleton  is  create"); // 创建单例的过程可能会比较慢
     4     }
     5 
     6     public static class SingletonHolder {
     7         private static Singleton instance = new Singleton();
     8     }
     9 
    10     public static synchronized Singleton getInstance() {
    11         return SingletonHolder.instance;
    12     }
    13 
    14 }

    没错,这就是现在最完整的单例写法,内部类实现单例,除了effctive java中阐述的枚举单例实现,这种方法是目前最好的实现方式。

    在这个实现中,用内部类来保护单例,当Singleton类被加载时,内部类不会被初始化,所以可以确保Singleton类被载入JVM时,不会初始化单例类,当getInstance方法被调用时,才会加载SingleHolder,从而初始化instance,同时,由于实例的建立是在类加载时完成的,故天生对多线程友好,getInstance()方法也不需要使用synchronized修饰,因此,这种实现能兼顾前两种写法的优点(延迟加载,非同步)。

     最后,在极端情况下,序列化和反序列化可能会破坏单例,一般来说不多见,如果存在就要多加注意,此时可以加入以下代码:

    private Object readResolve() {
    		return instance;
    	}
    

      

  • 相关阅读:
    Java中JNI的使用详解第四篇:C/C++中创建Java对象和String字符串对象及对字符串的操作方法 分类: Java 2013-12-27 12:39 2024人阅读 评论(0) 收藏
    Android中运行的错误:java.lang.UnsatisfiedLinkError: Couldn't load locSDK3: findLibrary returned null. 分类: Android 2013-12-26 15:29 21858人阅读 评论(10) 收藏
    使用VC6.0编译C++代码的时候报错:fatal error C1071: unexpected end of file found in comment(Mark ZZ) 2013-12-24 13:12 737人阅读 评论(0) 收藏
    Android中onTouch方法的执行过程以及和onClick执行发生冲突的解决办法 2013-12-23 16:35 14333人阅读 评论(6) 收藏
    Java中JNI的使用详解第三篇:JNIEnv类型中方法的使用 2013-12-21 15:40 2565人阅读 评论(0) 收藏
    UIView常用的一些方法小记之setNeedsDisplay和setNeedsLayout
    IOS UIImage类方法总结
    iOS 如何选择delegate、notification、KVO
    ios 中 KVO
    IOS 设置Launch image停留时间
  • 原文地址:https://www.cnblogs.com/zhengyu940115/p/6652465.html
Copyright © 2011-2022 走看看