zoukankan      html  css  js  c++  java
  • 创建型模式之Singleton模式

    单例模式大概是最直观的一种设计模式了,尽管直观却不简单。

    数学与逻辑学中,singleton定义为“有且仅有一个元素的集合”,

    单例模式可以如下定义:“一个类有且仅有一个实例,并且自行实例化向整个系统提供”。

    我比较喜欢Design Patterns 一书中的描述"保证一个类仅有一个实例,并提供一个访问它的全局访问点"。

    单例模式的特点

    1.单例类只能有一个实例

    2.单例类必须自己自己创建自己的唯一实例

    3.例类必须给所有其他对象提供这一实例

    单例模式的经典实现

    实现单例,可以将类的构造方法限定为private,避免在外部实例化,然后在类中提供一个静态的实例并能够返回给使用者,
    在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance方法访问。

    public class Singleton {
    
    /**
    * classic singleton
    * realize lazy loaded 添加判断uniqueInstance是否初始化,实现了使用时才进行加载
    */
    private static Singleton uniqueInstance=null;
    
    private Singleton(){
    //Exits only to defeat instantiation
    }
    public static Singleton getInstance(){
    if(uniqueInstance==null){
    uniqueInstance =new Singleton();
    }
    return uniqueInstance;
    }
    }

    线程安全的实现  

    经典的实现方法并没有考虑多线程的环境,试想存在两个线程A和B,
    同时调用getInstance方法,线程A检查uniqueInstance是null,开始创建实例;
    同时线程B检测到uniqueInstance是null,于是线程A/B各自创建了对象。

    解决这个问题最简单的方法是加锁,为getInstance的静态方法添加synchronized关键字,
    但是考虑到Synchronized同步锁的性能较低,可以调整Synchronized添加(加锁)的位置。

    public class SingletonSafed {
    
    	/**
    	 * 要在于instance = new Singleton()这句,这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面 3 件事情。
    	 * 给 instance 分配内存
    	 * 调用 Singleton 的构造函数来初始化成员变量
    	 * 将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了)
    	 * 使用volatile禁止指令重排序优化
    	 */
    	//在Java里所有引用类型的静态以及实例成员,没有显式地初始化的,都会被设为null
    	private volatile static SingletonSafed uniqueInstance;
    	
    	private SingletonSafed(){
    		//私有的构造方法,防止外部实例化
    	}
    	
    	//注意设置为静态方法
    	public static SingletonSafed getInstance(){
    		if(uniqueInstance== null){
    			/**
    			 * 同步块加锁。双重检查锁,因为会有两次检查 instance == null,一次是在同步块外,一次是在同步块内。
    			 * 为什么在同步块内还要再检验一次?因为可能会有多个线程一起进入同步块外的 if,如果在同步块内不进行二次检验的话就会生成多个实例。
    			 */
    			synchronized(SingletonSafed.class){
    				if(uniqueInstance== null){
    					uniqueInstance= new SingletonSafed();					
    				}
    				
    			}
    		}
    		    return uniqueInstance;
    	}
    }
    

      

    饿汉式 懒汉式和登记式

    另外,一些文档会提到单例模式的三种形式(懒汉式,饿汉式,登记式),
    其实饿汉式和懒汉式主要是线程安全的区别,同时懒汉式是延迟加载,
    在需要的时候才创建对象,而饿汉式在虚拟机启动的时候就会创建,例如下面代码:

    //饿汉式单例类.在类初始化时,已经自行实例化 
    public class Singleton1 {
    //私有的默认构造子
    private Singleton1() {}
    //已经自行实例化 
    private static final Singleton1 single = new Singleton1();
    //静态工厂方法 
    public static Singleton1 getInstance() {
    return single;
    }
    }
    

    懒汉式单例:

    //懒汉式单例类.在第一次调用的时候实例化 
    public class Singleton2 {
    //私有的默认构造子
    private Singleton2() {}
    //注意,这里没有final 
    private static Singleton2 single=null;
    //静态工厂方法 
    public synchronized static Singleton2 getInstance() {
    if (single == null) { 
    single = new Singleton2();
    } 
    return single;
    }
    }
    

    好文书签

    如何正确地写出单例模式

    聊聊并发(二)——Java SE1.6中的Synchronized

  • 相关阅读:
    poj2774
    GDOI2012 字符串
    poj3261
    poj1743
    bzoj 2565 manacher
    归档-ios
    学习
    ViewPager动态加载、删除页面
    android:ScrollView嵌套ListView的问题
    Android学习笔记进阶之在图片上涂鸦(能清屏)
  • 原文地址:https://www.cnblogs.com/binyue/p/3696565.html
Copyright © 2011-2022 走看看