zoukankan      html  css  js  c++  java
  • 如何创建一个对象(二、单例)

    为什么需要单例模式

    在应用程序中,经常会用到单例模式,即这个类只能存在一个对象实例。
    那么为什么需要这种模式,我们在一个程序应用中,只需要创建一次性的对象实例以节省内存资源,避免重复创建的开销,以便后面使用可以更快的访问。

    如何写一个单例模式

      单例作为所有设计模式中最简单的设计模式之一,其创建是非常简单的。

    饿汉式单例

    #code 饿汉式单例-不推荐
    public final class HungrySingleton {
    
    	private byte[] data = new byte[1024];
    
    	private static HungrySingleton instance = new HungrySingleton();
    
    	private HungrySingleton() {
    	}
    
    	public static HungrySingleton getInstance() {
    		return instance;
    	}
    }
    

      以上就是一个典型的饿汉式单例模式,在我们调用HungrySingleton. getInstance()方法时,不仅仅获取了一个已经创建的对象,并且获取了已经初始化了的1k的类变量数据。
    即便在多线程环境下,instance也不会被创建两次,因为在类初始化的时候被收集进()方法,该方法就是同步的。
    但是这个有一个缺陷,instance在被类加载之后,很长一段时间都会被常驻在堆内存,如果这个一个轻量级的类,那倒是无所谓,但如果这个类比较重,那么这种创建方式就不太妥当。

    懒汉式单例

    #code 懒汉式单例-不推荐
    public final class LazySingleton {
    
    	private static LazySingleton instance = null;
    
    	private LazySingleton() {
    	}
    
    	public static synchronized LazySingleton getInstance() {
    		if (null == instance) {
    			instance = new LazySingleton();
    		}
    		return instance;
    	}
    }
    

    上面这一段代码其实就是懒汉式单例,我们在真正调用getInstance()方法的时候才去创建这个实例,这个类所需的资源到这个时候才会被开辟。我们同时也使用synchronized来保证多线程环境下只有一份实例。
    看起来很美妙,但可惜的是,这个方式也同样不被推荐。原因也很简单,因为我们在使用getInstance()的时候是同步的,意味着每个调用该方法的线程都必须阻塞等待其他线程调用完成,这一点就很耗费性能。

    Double Check

    #code 双重检查式单例-不推荐
    public final class DCSingleton {
    
    	private static volatile DCSingleton instance;
    
    	private DCSingleton() {
    	}
    
    	public static DCSingleton getInstance(){
    		if (null == instance){
    			synchronized (DCSingleton.class){
    				if (null == instance){
    					instance = new DCSingleton();
    				}
    			}
    		}
    		return instance;
    	}
    }
    

    在我很长的一段时间内以来,在创建单例实例的时候都喜欢使用这种方式。它既保证了线程安全,也保证了延迟加载,同时相比懒汉式单例的耗费性能,它使用的双重检查的技巧很大程度上缓解了性能浪费,而且volatile修饰的instance也不会被指令重排困扰。看上去很完美,从一定程度上来说的确是这样。
    直到我看了doug lea与人合著的那本并发实践书籍,原文是这样的:“DCL这种使用方法已经被广泛的废弃了——促使该模式出现的驱动力不复存在,因而它不是一种高效的优化措施。延迟初始化占位类模式能带来同样的优势,并且更容易理解。”这里的驱动力是指,在新的版本下,无竞争同步的执行速度变快了(以前很慢),JVM的启动速度也变快了(以前很慢)。

    延迟初始化占位类模式

    #code Holder式单例-推荐max
    public final class HolderSingleton {
    
    	private HolderSingleton() {
    	}
    
    	private static class Holder {
    		private static HolderSingleton instance = new HolderSingleton();
    	}
    
    	public static HolderSingleton getInstance() {
    		return Holder.instance;
    	}
    }
    

    从线程安全、高性能、懒加载来看,这个应该是目前最好的单例设计之一,也是使用最为广泛的一个。

    枚举式

    #code Holder式单例-酌情使用
    public enum EnumSingleton {
    	INSTANCE;
    
    	EnumSingleton() {
    		System.out.println("complete init...");
    	}
    
    	public static EnumSingleton getInstance() {
    		System.out.println("getInstance...");
    		return INSTANCE;
    	}
    
    	public static void doneTask() {
    		System.out.println("doneTask...");
    	}
    }
    

    在《Effective Java》那本书中,这个枚举方式实现单例的方式就被极为推荐。枚举方式不允许被继承,同样也只是被实例化一次,但是不能够懒加载。所以读者可以酌情使用。

  • 相关阅读:
    Linux基础-4磁盘管理
    Linux基础-3文本处理
    Linux基础-2文件及目录管理
    Linux基础-1使用命令帮助
    解决Eclipse 添加 Tomcat 中的一些问题
    非关系型数据库 -- redis
    Java 学习笔记 五 -- Jedis
    Java 学习笔记 四 -- DBUtils的使用
    Java 学习笔记 三 -- 数据库连接池 Druid
    Java 学习笔记 二 -- JDBC事务
  • 原文地址:https://www.cnblogs.com/sally-zhou/p/10193292.html
Copyright © 2011-2022 走看看