设计模式目录:
概述:
第1 部分 问题引入
有时候某些对象我们只需要一个,如:线程池、缓存、对话框等等,对于这类对象我们只能有一个实例,如果我们制造出多个实例,就会导致很多问题产生。
但是我们怎样才能保证一个类只有一个实例并且能够便于访问?这里我们想到了全局变量,全局变量确实是可以保证该类可以随时访问,但是它很难解决只有一个实例问题。最好的办法就是让该自身来负责保存它的唯一实例。这个类必须要保证没有其他类来创建它。这里我们可以将其构造方法私有化。即
Public MyClass{
PrivateMyClass(){}
}
含有私有化构造器的类就能保证它不能被其他类实例化了。但是我们如何来获取这个实例化类呢?提供一个方法用于返回该类的实例对象即可实现。
public class MyClass { private MyClass(){ } public static MyClass getInstance(){ return new MyClass(); } }
第2 部分 模式定义和实现
单件模式确保一个类只有一个实例,并提供一个全局访问点。
从上面可以看出单例模式有如下几个特点:1、它只有一个实例。 2、它必须要自行实例化。3、它必须自行想整个系统提供访问点。
模式结构:
模式实现:
1 package firsthead.singleton; 2 3 /** 4 * 5 * @ClassName: Singleton 6 * TODO 7 * @author Xingle 8 * @date 2014-8-29 上午9:21:56 9 */ 10 11 //NOTE: This is not thread safe! 12 public class Singleton { 13 14 private static Singleton uniqueInstance; 15 16 //这里是其他有用的实例化变量 17 18 private Singleton() {} 19 20 public static Singleton getInstance(){ 21 if(uniqueInstance == null){ 22 uniqueInstance = new Singleton(); 23 } 24 return uniqueInstance; 25 } 26 27 //这里是其他有用的方法 28 29 }
在《Head First》有这样一个场景,就是说有两个线程都要执行这段代码,很有可能会产生两个实例对象。如下图:
处理多线程,只要把getInstance() 变成同步(synchronized )方法,多线程问题就几乎可以轻易地解决了:
1 //NOTE: This is not thread safe! 2 public class Singleton { 3 4 private static Singleton uniqueInstance; 5 6 //这里是其他有用的实例化变量 7 8 private Singleton() {} 9 10 public static synchronized Singleton getInstance(){ 11 if(uniqueInstance == null){ 12 uniqueInstance = new Singleton(); 13 } 14 return uniqueInstance; 15 } 16 17 //这里是其他有用的方法 18 19 }
但是,同步会减低性能。其实只有第一次执行此方法时,才真正需要同步。换句话说,一旦设置好uniqueInstance 变量,就不需要同步这个方法了,之后每次调用这个方法,同步都是一种累赘。
为了要符合大多数Java应用程序,需要确保单件模式能再多线程的状况下正常工作。但是同步getInstance() 的做法将拖垮性能,该怎么办呢?
这里有一些选择:
1.如果getInstance() 的性能对应用程序不是很关键,就什么都别做。
同步getInstance() 的方法既简单又有效,但是同步一个方法可能造成程序执行效率下降100倍。因此,如果将getInstance() 的程序使用在频繁运行的地方,你可能就要重新考虑了。
2. 使用“急切”创建实例,而不用延迟实例化的做法
如果应用程序总是创建并使用单件实例,或者在创建和运行时方面的负担不太繁重,你可能想要急切创建此单件,如下:
1 public class Singleton { 2 3 //在静态初始化器中创建单件。这段代码保证了线程安全 4 private static Singleton uniqueInstance = new Singleton(); 5 6 private Singleton() {} 7 8 public static Singleton getInstance() { 9 //已经有实例了,直接使用它 10 return uniqueInstance; 11 } 12 }
利用这个做法,依赖JVM在加载这个类时马上创建此唯一的单件实例。JVM保证在任何线程访问uniqueInstance 静态变量之前,一定先创建此实例。
3.用“双重检查加锁”,在getInstance()中减少使用同步
利用双重检查加锁,首先检查是否实例已经创建了,如果尚未创建,“才”进行同步。这样,只有第一次会同步,这正是我们想要的。
1 public class Singleton { 2 3 //volatile 关键词确保,当uniqueInstance 变量被初始化成Singleton实例时,多个线程正确地处理uniqueInstance 4 private volatile static Singleton uniqueInstance; 5 6 private Singleton() {} 7 8 public static Singleton getInstance() { 9 //检查实例,如果不存在,就进入同步区块 10 //只有第一次才彻底执行这里的代码 11 if (uniqueInstance == null) { 12 synchronized (Singleton.class) { 13 //进入区块后,再检查一次。如果仍为null,才创建实例 14 if (uniqueInstance == null) { 15 uniqueInstance = new Singleton(); 16 } 17 } 18 } 19 return uniqueInstance; 20 } 21 22 }
注意:双重检查加锁不适用于1.4及更早版本的java
第3 部分 示例
下面代码给出一个实例
Singleton.java
1 package firsthead.singleton; 2 3 /** 4 * 5 * @ClassName: Singleton 6 * TODO 7 * @author Xingle 8 * @date 2014-8-29 上午9:21:56 9 */ 10 public class Singleton { 11 12 protected static Singleton uniqueInstance; 13 14 // other useful instance variables here 15 16 protected Singleton() {} 17 18 public static synchronized Singleton getInstance() { 19 if (uniqueInstance == null) { 20 uniqueInstance = new Singleton(); 21 } 22 return uniqueInstance; 23 } 24 25 // other useful methods here 26 }
CoolerSingleton.java
1 public class CoolerSingleton extends Singleton{ 2 // useful instance variables here 3 protected static Singleton uniqueInstance; 4 5 private CoolerSingleton() { 6 super(); 7 } 8 9 // useful methods here 10 }
HotterSingleton.java
1 public class HotterSingleton extends Singleton{ 2 3 // useful instance variables here 4 5 private HotterSingleton(){ 6 super(); 7 } 8 9 // useful methods here 10 }
SingletonTestDrive.java
1 public class SingletonTestDrive { 2 3 public static void main(String[] args){ 4 Singleton foo = CoolerSingleton.getInstance(); 5 Singleton bar = HotterSingleton.getInstance(); 6 7 System.out.println(foo); 8 System.out.println(bar); 9 } 10 11 }
结果: