设计模式与实践:创建型模式(单例模式)

创建型模式

  • 单例模式
  • 工厂模式
  • 建造者模式
  • 原型模式
  • 对象池模式

单例模式

单例模式(Singleton pattern)是最常用的设计模式,它具有易于理解、使用简便等特点。有时候单例模式会过度使用或者在不合适的场景下使用,这样造成弊大于利的后果。

单例模式,顾名思义,用来保证一个对象只能创建一个实例,此外,它还提供了对实例的全局访问方法。

在这里插入图片描述
单例模式的实现很简单,只需要由单个类组成。为确保单利实例的唯一性,所有的单利构造器都需要被声明为私有的(private),再通过声明静态(static)方法实现全局访问获得该单例实例。

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

	public vid doSomething()
	{
		//TODO
	}
}

当我们在代码中使用该单例对象的时候,调用方式如下:

Singleton.getInstance().doSomething();

1. 同步锁单例模式

单例模式的实现很简单且高效,但是在多线程中的应用却不能这样随意,如果实例为空,那么就会有出现两个或者多个线程同时调用getInstance()的情况。这就会出现实例化多个对象的情况出现。

解决办法很简单,我们只需要创建一个代码块来检查实例是否空线程安全。

  • 向getInstance方法的声明中添加synchronized关键字来保证其线程安全:
public static synchronized Singleton getInstance(){
	//TODO
}
  • 用synchronized代码块包装 if( instance == null ) 条件。在这一环境中使用synchronized代码块时,需要制定一个对象来提供锁,Singleton.class对象就起这种作用。
synchronized (Singleton.class){
	if(instance == null){
		instance = new Singleton();
	}
}

2. 拥有双重校验锁机制的同步锁单例模式

上一个实现方式可以保证线程安全,但同时带来了延迟。用来检查实例是否被创建的代码块是线程同步的,也就是这个代码块同一时刻只能被一个线程执行,但是同步锁(locking)只有在实例没有被创建的情况下才起作用,如果单例实例已经被创建了,那么任何线程都希望能够用非同步的方式获取当前的实例。

只有在单例模式为实例化的情况下,才能在synchronized代码块前添加附加条件移动线程安全锁:

if(instance == null){
	synchronized(Singleton.class){
		if(instance == null){
			instance = new Singleton();
		}
	}
}

这是个很聪明的校验方式,注意到instance==null条件被检查了两次,因为我们需要保证synchronized代码块中也要进行一次检查

3. 无锁的线程安全单例模式

如何不通过锁来使得单例模式线程安全呢?

Java中单例模式的最佳实践中,类只会加载一次,通过声明时直接实例化静态成员的方式来保证一个类只有一个实例。这种实现方式避免了使用同步锁和判断实力是否被创建的额外检查:

public class LockFreeSingleton{
	private static final LockFreeSingleton instance = new LockFreeSingleton();
	private LockFreeSingleton(){
		//TODO
	}

	//这里的synchronized的意义在哪里呢?《设计模式与实践》书中有synchronized,但是笔者暂时没读懂这个synchronized的意义
	public static synchronized LockFreeSingleton getInstance(){
		return instance;
	}
	public void doSomething(){
		//TODO
	}
}

4. 提前加载和延迟加载

上述方法,在早期版本的Java中被认为是提前加载单例模式,但是在新版本的Java中,类只有在使用的时候才会被加载,所以它算是一种延迟加载模式。此外,类加载的时机主要取决于JVM的实现机制,因而版本之间会有不同。所以进行设计的时候,要避免与JVM的实现机制进行绑定。

如果确实需要提前实例化,可以在程序的开始通过调用getInstance方法强制执行。

Singleton.getInstance();

参考(学习笔记来自于):
《Java设计模式与实践》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章