设计模式之单例模式三(更好的实现方式)

          大学之道,在明明德,在亲民,在止于至善。 《礼记  大学》

       

 今天考完了安卓这门课,100分钟的考试50分钟就交卷离开考场了吻吻吻吻,回想一下大学生活就这么没了,虽然小菜现在还是大三但是大四学校安排是实习到下年四月份,学校里面已经没有了课程。想想自己的青春就这么没了,也是有点伤感呢。。最近在看《研读设计模式》这本书感觉写的很不错,通俗易懂哈。

         之前写过懒汉式单例模式在多线程环境下,会造成不只创建出一个实例的问题,下面是提出的几种解决方式以及他们分别出现的问题:

  1.  使用双重检验锁;   问题:JVM为了使得处理器内部的运算单元能充分利用,处理器可能会对输入代码进行乱序执行
  2. 在双重检验锁的基础上给对象加volatile修饰用来解决乱序执行的问题。问题:volatile关键字可能会屏蔽掉虚拟机中一些必要的代码优化,所以运行效率不会很高。所以在项目开发中这种方式也不要大量使用。

    那么既然上面的解决方式都有一些小小的瑕疵,那么还有什么比较合适的方式呢?

其中的一种方式就是使用类级静态内部类。第二种方式就是使用使用枚举类型。

    在多线程中,为了解决并发问题,我们主要是使用synchronized来加互斥锁进行同步。有些时候我们的jvm默认的已经为我们解决了多线程情况下的并发问题。其中包括:

  1. 静态初始化器初始化静态字段,代码块的时候
  2. 访问final字段的时候

采用静态初始化器初始化对象,这种方式很好理解,比如jvm在加载一个类时对其静态属性进行初始化的过程就是线程安全的,比如饿汉式单例模式中就使用的这种方式初始化对象的。但是这种方式是比较消耗资源的,因为它不管我们使不使用这个对象在加载类的时候它都会被创建出来从而占用一些不必要的内存。我们可以去假设一种方式让静态初始化器在装载类的时候不去初始化对象。

我们其实可以使用类级静态内部类的方式解决这个问题,为了更好的说明这个问题,我们先看下面这个例子

public class SingleIntance {

	private static class SingleHandler {
		private static SingleIntance singleIntance = new SingleIntance();
	}

	public void doSpeak() {
		System.out.println("I am a coder");
	}

	private SingleIntance() {

	}

	public static SingleIntance getSingleInstance() {
		return SingleHandler.singleIntance;
	}

	public static void main(String[] args) {
		SingleIntance intance = SingleIntance.getSingleInstance();
		intance.doSpeak();
	}
}

 这是这本书上给出的方案,我仔细揣摩感觉这种方式非常不错,也希望和大家分享一下这种方式。首先分析一下这种方式:SingleIntance这个类在进行装载的时候并不会装载SingleHandler这个静态内部类,当我们在第一次调用getSingleInstance这个方法的时候。SingleHandler.singleIntance这行代码会让虚拟机加载SingleHandler 这个内部类,并初始化其静态属性,虚拟机只会对一个类初始化一次,所以这样虚拟机就保证了单例对象只被初始化一次。这种方式的好处就是getSingleInstance并没有被同步,对其进行延迟加载并没有带来任何的访问成本,效率大大增强。

       第二种方式就是使用枚举来实现单例模式,按书中的说法《高效java第二版》中说道:单元素的枚举类型已经成为实现SingleIntance的最佳方式。java中的枚举类型启示是功能齐全的类可以有字段和方法,以前小菜只用到了枚举中的属性,方法并没有用到。看来好多知识都需要再补补了。java枚举类型的基本思想是通过共有的静态final域为每个枚举常量导出实例的类。下面看一个非常简单的例子:

public enum EnumSingleInstance {

	/**
	 * 定义枚举元素,该元素就是一个单例的对象
	 */
	singleInstance;
	
	public void doSpeak(){
		System.out.println("I am a single object");
	}
	
	public static void main(String[] args) {
		EnumSingleInstance instance=EnumSingleInstance.singleInstance;
		instance.doSpeak();
	}
}

 

 由以上代码可以看到用枚举来控制单实例的方式比其他方式会便捷很多,而且还无偿的提供了序列化的支持,由JVM保证只有一个实例,防止有多个实例。

现在是凌晨1:26好晚了,洗洗睡了。

猜你喜欢

转载自dxz.iteye.com/blog/2220995
今日推荐