一、简单介绍
单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。
《设计模式》书中对单例模式动机的介绍
对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。
如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。
二、类图
三、实现
主要有两点需要特别注意:
一点是构造方法必须是private的,否则不能保证只有一个实例
另一个是必须有pulic static的获取实例方法或者public static的实例属性,保证外部能访问
1、最基本的懒汉方式实现,线程不安全
package com.designpattern.singleton;
/**
* 懒汉,线程不安全
*
* @author chao
*
*/
public class Singleton1 {
private static Singleton1 instance;
private Singleton1() {
}
public static Singleton1 getInstance() {
if (instance == null) {
instance = new Singleton1();
}
return instance;
}
}
2、在public static Singleton1 getInstance()方法上加上同步关键字synchronized就能保证线程安全,但是影响了执行效率。
3、比较常用的高效的线程安全的单利模式实现是加双重锁
package com.designpattern.singleton;
/**
* 线程安全的单例模式,双重锁校验
*
* @author chao
*
*/
public class Singleton3 {
private volatile static Singleton3 instance;
private Singleton3() {
}
public static Singleton3 getSingleton() {
if (instance== null) {
synchronized (Singleton3.class) {
if (singleton == null) {
instance = new Singleton3();
}
}
}
return instance;
}
}
需要特别注意的是一定要加volatile关键字,这样才能保证在内存中访问到的是最新值,从而有效的做双重判断,保证只有一个instance对象。
4、饿汉模式,类初始化的时候创建
package com.designpattern.singleton;
/**
* 饿汉方式创建
*
* @author chao
*
*/
public class Singleton4 {
private static Singleton4 instance = null;//也可以直接对instance赋值,效果相同
static {
instance = new Singleton4();
}
private Singleton4() {
}
public static Singleton4 getInstance() {
return instance;
}
}
这种方式有很大的缺点,就是不能接收外部参数,getInstance方法一定是无参的。因为在getInstance方法之前,其实instance已经有了实例。
这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。
5、饿汉模式改进版,利用内部类实现懒加载
/**
* 懒加载的饿汉单例模式
*
* @author chao
*
*/
public class Singleton5 {
private static class SingletonHolder {
private static final Singleton5 INSTANCE = new Singleton5();
}
private Singleton5() {
}
public static final Singleton5 getInstance() {
return SingletonHolder.INSTANCE;
}
}
这种方式Singleton类即使被装载了,instance也不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,而且实现了懒加载。但是getInstance方法同样不支持传参数。
6、枚举实现单例模式
以上方法都有一个问题,就是不能方式反射方法破坏单例模式。如果使用反射,还是可以创建多个实例的。
Joshua Bloch的《Effective Java》一书中提到了单例模式枚举的实现,保证只有一个实例(即使使用反射机制也无法多次实例化一个枚举量),并且线程安全。
看起来很好,但是很少见到真正有这么用的。
package com.designpattern.singleton;
/**
* 利用枚举实现单利
*
* @author chao
*
*/
public enum Singleton {
INSTANCE;
public Object buid() {
return new Object();
}
}
四、JDK和Android SDK中的单例模式
java.lang.Runtime.getRuntime(),NumberFormat
android全局Application,
android中的各种Manager,系统service,比如InputMethodManager ,ActivityManagerService等
相关代码github地址:https://github.com/robertjc/simpledesignpattern
不断完善中,有问题请多指教
欢迎扫描二维码,关注公众账号