1. 定义: 单例模式就是确保一个类中只有一个实例,并且该实例必须自动创建,并向整个系统提供该实例。
2. 使用时机: 当系统要求一个类只有一个实例时,就需要使用用单例模式。
单例模式是作为对象的创建模式,有三个特点:
1)该类只有一个实例
2)该类自行创建该实例(在该类内部创建自身的实例对象)
3)向整个系统公开这个实例接口
class Singleton { //私有,静态的类自身实例 private static Singleton instance = new Singleton(); //私有的构造子(构造器,构造函数,构造方法) private Singleton(){} //公开,静态的方法 public static Singleton getInstance() { return instance; } }
这个单例类在自身被加载时instance会被实例化,即便加载器是静态的。因此,对于资源密集,配置开销较大的单体更合理的做法是将实例化(new)推迟到使用它的时候。即惰性加载(Lazy loading),它常用于那些必须加载大量数据的单体。修改下:
class LazySingleton { //初始为null,暂不实例化 private static LazySingleton instance = null; //私有的构造子(构造器,构造函数,构造方法) private LazySingleton(){} //公开,静态的工厂方法,需要使用时才去创建该单体 public static LazySingleton getInstance() { if( instance == null ) { instance = new LazySingleton(); } return instance; } }
class LazySingleton { //初始为null,暂不实例化 private static LazySingleton instance = null; //私有的构造子(构造器,构造函数,构造方法) private LazySingleton(){} //公开,静态的工厂方法,需要使用时才去创建该单体 public static synchronized LazySingleton getInstance() { if( instance == null ) { instance = new LazySingleton(); } return instance; } }
不过这种简单的写法不适合用于像服务器这种服务很多线程的程序上,同步机制会造成相当的效能低落,为了顾及 Singleton 、 Lazy Initialization 与效能问题,因而有了 Double-check Locking 的模式:
double-checked locking
public class Singleton{ private Singleton(){} public static Singleton getInstance(){ if(single==null){ synchronized(Singleton.class){ if(single==null){ single=new Singleton(); } } } return single; } private volatile static Singleton single; }
也就是只有在第一次建立实例时才会进入同步区,之后由于实例已建立,也就不用进入同步区进行锁定。
问题:Double checked lock也还是不能保证线程安全的。在多核机或者某些jvm实现上,由于指令乱序执行的可能性,还是有可能导致生成多个实例。。。
另一种实现方式
class Singleton{
class SingletonHolder{
private static Singleton instance = new Singleton()
}
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
public class Singleton { private static Singleton instance = null; public static Singleton getInstance() { if(instance == null) { instance = SingletonLazy.lazy; } return instance; } private static class SingletonLazy { public static Singleton lazy = new Singleton(); } }
classloader首先会加载Singleton.class文件,运行到if(instance==null)这句的时候,如果为空,回去加载SingletoneLazy.class。如此便实现了lazy初始化。
枚举实现:
package com.flyingh.test3;
public enum Singleton {
singleton;
}
Java 中 Runtime 类别的作法简单的多, 它舍弃了 Lazy Initialization ,如果您要取得单例的机会不是很多,可以用这种方式:
package org.bestupon.dp.singleton;
/**
*
* @author BestUpon
* @email bestupon@foxmail.com
* @date 2010-6-13上午11:08:28
* @ask jdk中Runtime这个类似就是一个单例模式的应用:
*
* @answer
*/
public class Test4RunTime {
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
runtime.freeMemory();
}
}
public class Runtime {
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
/** Don't let anyone else instantiate this class */
private Runtime() {}
// 以下略
}
Singleton 本身的观念简单但应用 很广,因而很多时候必须对实际环境作一些考虑与调整。
总结:
单例模式可以分为两种:饿汉式和懒汉式两种,饿汉是在系统启动的一开始就初始化好了实例,而懒汉式是在第一次访问的时候才初始化实例。
HungerSingleton & LazySingleton
优点:
在单利模式中,客户调用类的实例时,只能调用一个公共的接口,这就为整个开发团队提供了共享的概念
缺点:
单利模式在实例化后,是不允许类的继承的;在分布式系统中,当系统的单利模式类被复制运行在多个虚拟机下时,在每一个虚拟机下都会创建一个实例对象,此时如果想知道具体哪个虚拟机下运行着单例对象是很困难的,而且单例类是很难实现序列化的。
疑问:
1)好像单例的特性都可以通过类的静态方法和静态属性实现,为什么还要有单例模式呢?
2)
由于端午节放假的缘故,各位同仁在期间提出了很多的问题,也指出了我思维的局限性,没有及时的更新于回复各位好友的,深表遗憾!
针对单例模式的很多用法,前面一直是在所单一线程的问题,本来设计多线程的问题就很少,也对双重锁定这个问题没有深入的五挖掘,导致了犯了今天这样的错误。 之后参考了一些资料,针对各位朋友提出的问题与我自身存在的问题,进行改进! 参考的文章是:IBMDeveloperWorks(中国)的网站上的文章 《 双重检查锁定及单例模式 》 ( http://www.ibm.com/developerworks/cn/java/j-dcl.html )。这篇文章真针对各种问题都有分析,包括可见性等问题。得出了一个结论:双重锁定失效的主要原因是:不同JVM之间的无序写入问题,多线程之间的独占、休眠(记忆复苏)所引起的不同 不问题。
最终本文提出了一个建议: 建 议不要使用“双重锁定”! 一个解决单例模式的方案:
底线就是:无论以何 种形式,都不应使用双重检查锁定,因为您不能保证它在任何 JVM 实现上都能顺利运行。JSR-133 是有关内存模型寻址问题的,尽管如此,新的内存模型也不会支持双重检查锁定。
未完待续。。。