设计模式之单例模式
目的:希望对象只创建一个实例,并且提供一个全局的访问点。
使用场景: 要求只能有一个实例。比如打印机服务。多个电脑或其他设备共享一个打印机,但同一时间只能有一个输出。
还有序列生成器,整个应用必须是唯一的,只能有一个实例;windows任务管理器和回收站等。
/**
* 饿汉式单例,类加载的时候就初始化
*
*/
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return instance;
}
}
/**
* 懒汉式单例,只有第一次调用的时候才初始化
*/
public class SingletonA {
private static SingletonA instance = null;
private SingletonA(){
}
public static SingletonA getInstance(){
if(instance==null){
instance = new SingletonA();
}
return instance;
}
}
/**
* 懒汉式单例,只有第一次调用的时候才初始化。线程安全控制,采用synchronized关键字
*/
public class SingletonB {
private static SingletonB instance = null;
private SingletonB(){
}
public synchronized static SingletonB getInstance(){
if(instance==null){
instance = new SingletonB();
}
return instance;
}
}
增加了synchronized关键字后,会影响性能。多线程时大部分时间都用来synchronized关键字的准备上。所以有了double check lock(双重锁定检查)
/**
* 懒汉式单例,只有第一次调用的时候才初始化。线程安全控制,采用DCL(double check lock)
*/
public class SingletonC {
private static SingletonC instance = null;
private SingletonC(){
}
/**
* DCL(双重锁定检查),可很大程度上减轻因同步带来的性能消耗,只在instance为null的时候才同步。
*
*/
public static SingletonC getInstance(){
if(instance==null){
synchronized(SingletonC.class){
if(instance==null){
instance = new SingletonC();
}
}
}
return instance;
}
}
我们来看看这个场景:假设线程一执行到instance = new SingletonC()这句,这里看起来是一句话,但实际上它并不是一个原子操作(原子操作的意思就是这条语句要么就被执行完,要么就没有被执行过,不能出现执行了一半这种情形)。
事实上高级语言里面非原子操作有很多,我们只要看看这句话被编译后在JVM执行的对应汇编代码就发现,这句话被编译成8条汇编指令,大致做了3件事情:
1.给SingletonC的实例分配内存。
2.初始化SingletonC的构造器
3.将instance对象指向分配的内存空间(注意到这步instance就非null了)。
但是,由于Java编译器允许处理器乱序执行(out-of-order),以及JDK1.5之前JMM(Java Memory Medel)中Cache、寄存器到主内存回写顺序的规定,上面的第二点和第三点的顺序是无法保证的,也就是说,执行顺序可能是1-2-3也可能是1-3-2,
如果是后者,并且在3执行完毕、2未执行之前,被切换到线程二上,这时候instance因为已经在线程一内执行过了第三点,instance已经是非空了,
所以线程二直接拿走instance,然后使用,然后顺理成章地报错,而且这种难以跟踪难以重现的错误估计调试上一星期都未必能找得出来。
如果JDK是1.5或之后的版本,只需要将instance的定义改成“private volatile static SingletonC instance = null;”就可以保证每次都去instance都从主内存读取,就可以使用DCL的写法来完成单例模式。
还有一种实现单例的方法是采用ThreadLocal。
/**
* 采用ThreadLocal实现单例,每个线程有一个单例
*/
public class SingletonD {
private static ThreadLocal<SingletonD> singletonDHolder = new ThreadLocal<SingletonD>();
private SingletonD(){
}
public static SingletonD getInstance(){
if(singletonDHolder.get()==null){
singletonDHolder.set(new SingletonD());
}
return singletonDHolder.get();
}
}
分享到:
相关推荐
java设计模式之单例模式
JAVA设计模式之单例模式。 一篇文章带你快速了解!
java设计模式之单例模式,通过例子说明8种单例设计模式。
研磨设计模式之单例模式,牛人精华之作!!!!
java设计模式之单例模式详解,包含例子,详解。
JAVA设计模式之单例模式(完整版)1[定义].pdf
php设计模式之单例模式_.docx
细心整合和单例模式和工厂模式的几种模型,懒汉式,饿汉式,如何并发操作模式,等都有详细讲解
php设计模式之单例模式代码_.docx
设计模式之单例模式,单列模式的几种实现形式,以及其优缺点,还有就是示例,对初步了解单列模式的有所帮助
php设计模式之单例模式实例分析_.docx
PHP设计模式之单例模式__1.docx
单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。 许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。 比如在某个服务器程序中,该服务器的配置信息存放...
JAVA设计模式之单例模式(完整版)[归类].pdf
目录 单例模式的概念 单例模式的要点 单例模式类图 单例模式归类 单例模式的应用场景 单例模式解决的问题 单例模式的实现方式 单例模式实现方式对比 单例模式的概念 单例模式,顾名思义就是只有一个实例,并且由它...