目录
1、单例(Singleton)模式:
2、最简单的单例模式:
3、进阶:
4、延迟创建 :
5、线程安全 :
6、如何创建并发访问效率高的单例 : Double-Check Locking
7、完整的测试用例如下:
8、Initialization on demand holder
9、单例模式序列化应该注意的问题: Singleton 的序列化
10、推荐阅读:
1、单例(Singleton)模式:
保证一个类在系统里只能有一个对象被实例化。
如:缓存池、数据库连接池、线程池、一些应用服务实例等。
难点:在多线程环境中,保证实例的唯一性。
2、最简单的单例模式:
保证该类构造方法是私有的,外部无法创建该类型的对象;
提供一个全局访问点,方便给客户对象提供对此单例对象的使用;
public class Singleton {
/**
* 私有变量,外界无法访问
* 可以定义 public 类型 instance变量,把属性直接暴露给客户对象,则没必要实现getInstance()方法
* 但是可读性降低,而且直接暴露实例变量的名字给客户程序,会增加代码的耦合度
*/
private static Singleton instance = new Singleton();
static {
//...
}
// 唯一的 private构造方法,客户对象无法创建该对象实例
private Singleton() {
}
// 全局访问点
public static Singleton getInstance() {
return instance;
}
}
// 客户使用单例模式代码
Singleton singleton = Singleton.getInstance();
如果该实例需要比较复杂的初始化过程时,把这个过程应该写在 static{ ... }代码快中。
注意:此实现是线程安全的,当对个线程同时去访问该类的 getInstance( ) 方法时,不会初始化多个不同的对象,这是因为,JVM 在加载此类时,对于 static 属性的初始化只能由一个线程执行且仅一次。
3、进阶:
Statci 在加载类时就会被初始化,出于性能等方面的考虑,我们希望延迟实例化单例对象,只有在第一次使用该类的实例时才去实例化。
4、延迟创建 :
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
我们把单例的实例化过程移至 getInstance( )方法,而不是在加载类时预先创建,当访问此方法时,首先判断该实例是不是已经被实例化过了,如果已被初始化,则直接返回这个对象的引用;否则,创建这个实例并初始化,最后返回这个对象引用。
使用 if (instance == null) 判断是否实例化完成了,此方法不是线程安全的。
编写高质量代码 改善Java程序的151个建议 PDF高清完整版 http://www.linuxidc.com/Linux/2014-06/103388.htm
Java 8简明教程 http://www.linuxidc.com/Linux/2014-03/98754.htm
Java对象初始化顺序的简单验证 http://www.linuxidc.com/Linux/2014-02/96220.htm
Java对象值传递和对象传递的总结 http://www.linuxidc.com/Linux/2012-12/76692.htm
Java对象序列化ObjectOutputStream和ObjectInputStream示例 http://www.linuxidc.com/Linux/2012-08/68360.htm
本文永久更新链接地址:http://www.linuxidc.com/Linux/2014-06/103535.htm
5、线程安全 :
在高并发的环境中,getInstance( ) 方法将返回多个指向不同的该类实例。
Thread 1 | Thread 2 | |
1 | if (instance == null) | |
2 | if (instance == null) | |
3 | Singleton instance = new Singleton(); | |
4 | Singleton instance = new Singleton(); | |
5 | return instance; | |
6 | return instance; |
在时刻 1和 2,由于还没有创建单例对象,Thread 1 和 Thread 2都会进入创建单例实例的代码块分别创建实例。在时刻 3 ,Thread 1创建了一个实例对象,但是 Thread 2此时已无法知道,于是继续创建一个新的实例对象,导致这两个线程持有的实例并非为同一个。
更为糟糕的是,在没有自动内存回收机制的语言平台上运行这样的单例模式,如:C++,以为我们认为创建了一个单例实例,忽略了其他线程所产生的对象,不会手动去回收它们,从而引起内存泄漏。
为了解决这个问题,我们给次方法添加 关键字,代码如下:
public static synchronized Singleton getInstance() {
if ( instance == null ) {
instance = new Singleton();
}
return instance ;
}
这样,再多的线程访问都只会实例化一个单例对象,实现了多线程的安全访问,但是在多线程高并发访问的情况下,给此方法加上 synchronized 关键字会是得性能大不如前。
更多详情见请继续阅读下一页的精彩内容: http://www.linuxidc.com/Linux/2014-06/103542p2.htm