设计模式学习之路 - 单例模式 - only you

原创 2016年08月28日 20:03:38

今天我们了解一下单例模式,这个模式似乎是笔试最多的模式之一了(面试几乎必问, 似乎面试官特别感兴趣)

单例,从字面意思看, 就是单独的实例, 表示这个实例是唯一的。

那么很多人就会问了,为什么需要这种只有一个实例的类。

其实,在开发中, 很多对象我们都只需要一个,比如:线程池、缓存等等.

实际上这些对象也有且只能有一个,多个实例的话,反而会有问题,或为程序异常,或为资源不足。


我们先贴一下单例模式的定义: 确保一个类只有一个实例, 并提供一个全局访问点


单例模式的代码其实很简单,按照正常的思路会这样做。

package com.chris.single;

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

定义一个静态变量, 当变量存在就返回,不存在就直接创建一个,这样就只有一个实例了。


如果一般的情况,这段代码好像没有什么问题,能确保只有一个实例。

但是, 这时候又要扯到另一个面试必问的问题了, 多线程!!

当有多个线程去执行获取实例的方法时, 可能会同时会判断到uniqueInstance == null 这个条件,这时候就会同时创建实例,这个类就有两个实例了。

有两个实例就不是only you了, 就不是唯一了,所以我们需要对多线程的情况稍微处理一下。


一到同步的问题,大多数开发人员脑海中立刻回浮现出一个单词 : synchronized!!

的确, 只要用synchronized对方法进行修饰, 多线程的灾难几乎就可以轻易的解决。

package com.chris.single;

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

synchronized关键字,可以保证一个线程进入这个方法之后,对其加一个线程锁, 其他线程在这个线程离开这个方法之前,

都必须等待,不会有两个线程同时进入这个方法。


这个方法好像是操作起来最快的方法了, 加一个关键字进行修饰就可以解决。

但是,在很多书上都会提及, synchronized关键字, 是一个十分重量级的东西,会很大的影响性能,无论是修饰方法或者同步一个代码块。

所以,如果你的应用程序能够接受这个造成的额外的性能负担, 那么上述代码就是你想要的单例模式, 这个实现方式叫懒加载(lazy-init)!


或者, 你的应用程序在创建和运行时的负担不是很重, 这时候我们也可以使用更简便的方式, 急加载(eagerly-init)!

package com.chris.single;

public class Singleton {
	private static Singleton uniqueInstance = new Singleton();
	
	private Singleton(){
		
	}
	
	public static Singleton getInstance(){
		return uniqueInstance;
	}
}

这种方式会在程序编译后立即加载这个类并创建唯一的实例,而不是在运行时创建, 这样就可以保证只有一个实例。

但是这个类一直都没有被用到的话,这就会浪费空间了,这也是这个方式的一个缺点。


然后如果想综合考虑的话, 这里就有另一种解决办法, 双重校验锁(double-cheked locking)!

package com.chris.single;

public class Singleton {
	private volatile static Singleton uniqueInstance;
	
	private Singleton(){
		
	}
	
	public static Singleton getInstance(){
		if(uniqueInstance == null){
			synchronized (Singleton.class) {
				if(uniqueInstance == null){
					uniqueInstance = new Singleton();
				}
			}
		}
		return uniqueInstance;
	}
}
为什么叫双重, 因为他有两个关于同步的关键字, 一个是synchronized, 另一个是volatile.

这个方式会首先检查实例是否被创建,如果未创建,这时候才进行同步,所以,同步只会有一次,相比懒加载方式,这个会大大的减少时间耗费,提高性能。

这个方式好像看起来十分的完美了,但是任何介绍这个双重校验锁方式的读物,都会强调一个事情。

在1.4以及更早的版本中,许多JVM对volatile这个关键字的实现会导致双重校验的失效,所以如果不能用java5以后的版本,就不要用这个方式了(用4以前的几乎没有了吧!)


然后我们聊聊volatile,为什么要用这个关键字呢, 因为虽然synchronized能保证进入创建实例的代码是同步的,但是new Singleton() 这个操作缺却不是原子的。

在创建实例的时候,我们的JVM一般会做三件事:

1. 给uniqueInstance分配内存;

2.调用类的构造方法初始化成员变量;

3.将uniqueInstance对象指向分配的内存空间。

当操作3完成之后,其实这个对象就已经不为null了。

而JVM中的JIT(即时编译器)存在指令重排序的优化,所以,步骤2和步骤3的顺序是不能保证的。

最终可能为1-2-3, 也可能为1-3-2,而如果是后者的话,先完成了内存的分配,而这时候另一个线程进来了,判断实例不为null, 直接返回,

但是这个实例却没有经过第2步初始化, 程序就理所当然的报错了。

volatile这个关键字能保证修饰的参数对所有的线程都是可见的, 并且禁止指令重排序优化

对所有的线程可见,是表示线程中不会存在该实例的副本,每次需要拿的话都会去主内存中拿。

而禁止指令重排序优化,就如上述所说,能保证他的操作的执行顺序在JIT进行优化时不会被打乱。


在开发中,自己写单例好像也比较少,一般都是用的第三方的,所以只能了解一下大概的概念。

如果在文中有什么错误的地方,还望指正,和大家共勉。。。






Java学习笔记---设计模式之单例模式

一、引言         单例模式是设计模式中最常用的一种,也是工作面试中会被经常问到的一种,因此,这篇文章将主要讲述如何实现单例模式,以及单例模式的一些优缺点。 二、单例模式的定义      ...
  • wenwen091100304
  • wenwen091100304
  • 2015年11月07日 13:51
  • 640

Java中的设计模式学习总结(二)---单例模式

java的二十三种设计模式之单例模式。
  • wu631464569
  • wu631464569
  • 2016年09月25日 18:39
  • 214

架构师学习之路1设计模式

想学习基础在数据结构和设计模式之间犹豫了很久,最后还是决定先看一下常用的设计模式 参考书:Design Pattern-Head First 中文版 原则 封装变化 多用组合,少用继承 针对接口编程,...
  • zsr251
  • zsr251
  • 2015年06月30日 13:38
  • 406

设计模式的学习之路 --- 第一站(模板模式)

前言: 作为设计模式里面最简单的设计模式之一,却被广泛使用的。作为学习设计模式的开篇真=真是再适合不过了。 这个设计模式之中仅仅使用了继承关系。虽然Java里面因为单继承、多实现的原因不建议过多的...
  • qq_27232757
  • qq_27232757
  • 2017年09月14日 15:01
  • 61

设计模式系列(一)单例模式

一、简单介绍 单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。 《设计模式》书中对单例模式动机的介绍 对于系统中的某些类...
  • robertcpp
  • robertcpp
  • 2016年06月05日 11:00
  • 2225

C++设计模式[二]单例模式

接着就是单例模式;个人理解就是把创建方法改为私有,然后再内部实例化,禁止外部创建对象。 何为单例模式,在GOF的《设计模式:可复用面向对象软件的基础》中是这样说的:保证一个类只有一个实例,并提供一个...
  • langb2014
  • langb2014
  • 2015年11月03日 16:11
  • 1073

设计模式(创建型)之单例模式(Singleton Pattern)

单例模式可能是23种设计模式中最简单的。应用也非常广泛,譬如Android中的数据库访问等操作都可以运用单例模式。核心概念: 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称...
  • yanbober
  • yanbober
  • 2015年04月27日 16:50
  • 2655

使用Object-C实现23种设计模式之单例模式

今天给大家讲解一下第四种创建模式——单例模式,单例模式不同于前几种,前几种更注重逻辑,而单例模式则偏重于内存管理以及实际运用,相对前几种要相对简单。 所谓单例模式,就是一种让某个类只产生单一实例的模式...
  • u013054715
  • u013054715
  • 2016年05月23日 22:43
  • 4394

设计模式:单例模式(Singleton)

单例模式在23个设计模式中算得上是最简单的一个了,也行你会有异议,那就换成“最简单之一”,这样就严谨了很多。   单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。   适用性:当类...
  • u013256816
  • u013256816
  • 2016年03月23日 21:37
  • 4916

浅谈常见设计模式--单例模式 简单工厂模式

今题那站在这里和大家一起分享最近在一本书上看到的关于设计模式的内容,接下来要讲的设计模式有: 单例模式 简单工厂模式 工厂方法和抽象工厂 代理模式 命令模式 策略模式 门面模式 桥接模式 观察者模式 ...
  • xikai18827083487
  • xikai18827083487
  • 2016年11月13日 17:04
  • 966
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:设计模式学习之路 - 单例模式 - only you
举报原因:
原因补充:

(最多只允许输入30个字)