看了多方资料,整理下单例设计模式,有不少值得相互探究的地方,你就会发现就这一个小小的单例模式竟然映射出N多知识。我在这里把问题综述出来,一起相互探讨。
单例涉及到的相关文章如下:
反射、枚举与单例
序列化与单例
类加载器与单例
本文则主要是讲多线程与单例。
单例模式首先分为懒汉式和饿汉式。所谓饿汉式即一开始就创建出单例对象,懒汉式则为当需要使用的时候才会去创建出单例对象。
先看下饿汉式:
public final class Singleton {
private static final Singleton instance=new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
1 私有化构造器,使得别人无法再创建新对象。
问题1:即使私有化构造器,别人仍然可以通过反射机制来创建新对象,要是这样的话,下面的很多单例方法都不再是单例。然而枚举除外。
2 这种饿汉式的方式在类加载器加载Singleton的时候就会去初始化创建一个Singleton实例,类加载器加载Singleton时线程安全的,所以这种方式不存在线程安全问题。
懒汉式:有时候为了在使用的时候才去创建单例对象,就要采用懒汉式
public final class Singleton {
private static Singleton instance=null;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
instance=new Singleton();
}
return instance;
}
}
这种方式即在需要的时候才会去创建单例对象。很明显,大家都知道这种方式引入了线程安全问题,所以要对getInstance方法加上锁,如下:
public final class Singleton {
private static Singleton instance=null;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance==null){
instance=new Singleton();
}
return instance;
}
}
这样的话,每个线程要执行getInstance方法时,synchronized对他们进行了同步,保证并发情况下只有一个线程在执行getInstance方法。这种做法的的确解决了线程安全问题,但是却造成了很大的性能开销。因为instance只需要在第一次创建时进行同步,创建后每次获取时不需要再进行同步,所以我们要进一步改进:
public final class Singleton {
private static Singleton instance=null;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
synchronized (Singleton.class) {
instance=new Singleton();
}
}
return instance;
}
}
这种方式即缩小了同步的范围,保证了在单例对象创建出来后,每次获取时不需要再进行同步,但是又造成了一个问题,即不能保证instance=new Singleton()只被执行一次,所以又要改进,需要在同步的代码中再次检查是否已经创建,如下:
public final class Singleton {
private static Singleton instance=null;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
synchronized (Singleton.class) {
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
这就是所谓的双重检查机制。看似已经完美,实则不然。instance=new Singleton()实际上分为三个过程:
1 分配内存
2 对Singleton的一些初始化工作包括构造函数的执行
3 对instance变量赋值内存地址
然而对于第2步和第3步,不同的编译器由于执行了优化导致他们的执行顺序并不一致,即发生了重排序,对于重排序参见这篇infoq上的文章http://www.infoq.com/cn/articles/java-memory-model-2
也就是线程1当执行到第2步的时候,instance就已经有值了,此时线程2执行getInstance方法的最外层的if(instance==null)判断就会直接返回。然而该对象还没有真正的完成初始化,还不能正常使用。此时线程2如果去使用该对象,就会出问题了。
为了解决这个问题,就是不允许第2步和第3步进行重排序,使用volatile来解决,如下:
public final class Singleton {
private volatile static Singleton instance=null;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
synchronized (Singleton.class) {
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
只需要在Singleton instance变量上加上volatile修饰,就可以禁止重排序。我们知道synchronized 即保证可见性又保证了互斥性,而volatile则仅仅是保持了可见性,而这里volatile又起到禁止重排序的功能(我也不懂,留给大神们去研究)。
另一种解决方案是,基于类初始化的解决方案:
public final class Singleton {
private static class SingletonHolder{
public static Singleton instance=new Singleton();
}
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
这也是一种常见的懒汉式单例,接下来我们就要分析分析它是如何解决多线程问题的。
当多个线程执行SingletonHolder.instance时,会首先进行类的初始化,即多个线程可能同时去初始化同一个类,这方面对于jvm来说是进行了细致的同步,每个类都有一个初始化锁,来确保只能有一个线程来初始化类。当线程A获取了SingletonHolder类的初始化锁,线程B则需要等待,线程A就要去执行SingletonHolder的静态变量表达式、静态代码块等初始化工作,然后就能确保Singleton instance=new Singleton()只被一个线程来执行。
总的来说,此种方法是依靠jvm对类和接口的同步来实现单例线程安全的。具体jvm对于类和接口初始化的同步过程可以见这篇文章http://www.infoq.com/cn/articles/double-checked-locking-with-delay-initialization
若想转载请注明出处: http://lgbolgger.iteye.com/blog/2157820
作者:iteye的乒乓狂魔
分享到:
相关推荐
JavaScript中的单例模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个唯一实例。这种模式在JavaScript开发中非常有用,尤其是在处理全局变量、避免命名冲突、节省内存以及统一接口等...
Java单例模式和多例模式实例分析 单例模式是指这种设计模式,确保一个类只有一个实例,并提供一个全局访问点来访问该实例。单例模式的定义是:单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供一个...
在实际项目开发中,为了优化资源使用并提高代码效率,我们通常会采用设计模式来组织代码,其中单例模式就是一个典型的例子。本篇文章将详细解释如何在PHP中实现一个基于MongoDB的单例模式操作类。 首先,单例模式的...
单例模式:单例模式属于创建型模式,一个单例类在任何情况下都只存在一个实例,构造方法必须是私有的、由自己创建一个静态变量存储实例,对外提供一个静态公有方法获取实例。 这些设计模式都是软件开发人员需要了解...
"设计模式汇总_圣思园Java版"是一个专门针对Java程序员的资源集合,它包含了UML类图、Java源码和PDF文档,便于学习和理解各种设计模式。 1. **UML类图**:统一建模语言(UML)是一种图形化表示软件系统的方法,它...
这些模式都是在面向对象编程中经常遇到的,例如单例模式确保一个类只有一个实例,工厂模式则提供了一种创建对象的最佳方式,而观察者模式则用于定义对象间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于...
本项目汇总主要讨论了单例模式、一致性哈希算法以及页面静态化等关键知识点。 1. **单例模式**: 单例模式是一种常用的软件设计模式,确保一个类只有一个实例,并提供全局访问点。通常用于管理共享资源或昂贵的...
4. 多例模式:与单例模式类似,但允许有多个实例存在,控制实例的数量。在PHP中,可能会用在数据库连接池或者缓存管理中。 5. 对象池设计模式:通过重用已经创建的对象,避免频繁的创建和销毁对象带来的性能开销。...
所涉及的设计模式包括工厂模式、单例模式、建造模式、原型模式、适配器模式、桥梁模式、组合模式、装饰模式、门面模式、享元模式、代理模式、责任链模式、命令模式、解释器模式、迭代器模式、调停者模式、备忘录模式...
- 单例模式(Singleton):保证一个类只有一个实例,并提供全局访问点。 - 原型模式(Prototype):通过复制已有对象来创建新对象,而不是通过构造函数。 - 工厂方法模式(Factory Method):定义一个用于创建...
【单例模式】 单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供一个全局访问点。在Java中,常见的单例模式实现有三种: 1. **饿汉模式**:在类加载时就创建单例对象,线程安全,但会提前占用...
单例模式是指一个单例类在任何情况下都只存在一个实例,构造方法必须是私有的、由自己创建一个静态变量存储实例,对外提供一个静态公有方法获取实例。优点是内存中只有一个实例,缺点是可能会隐藏类之间的依赖关系。...
单例模式确保一个类只有一个实例,并提供一个全局访问点。在C++中,通过将构造函数设为私有并使用静态方法来创建和返回这个唯一的实例。而在C语言中,由于没有类的概念,我们可以使用全局变量和静态初始化来实现单例...
创建型模式关注对象的创建,如单例模式(Singleton)、工厂模式(Factory Method)和建造者模式(Builder)等。结构型模式处理对象组合和继承,如适配器模式(Adapter)、装饰器模式(Decorator)和代理模式(Proxy...
常见的创建型模式包括单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式。例如,单例模式确保一个类只有一个实例,并提供一个全局访问点。工厂方法模式使用一个工厂接口来创建对象,但由子类决定要实例化...
1. **单例模式**:确保一个类只有一个实例,并提供一个全局访问点。 2. **工厂模式**:定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。 3. **抽象工厂模式**:提供一个创建一系列相关或相互依赖...
1. **单例模式**:确保一个类只有一个实例,并提供一个全局访问点。UML类图中,单例类通常有一个私有的构造函数和一个静态方法来返回唯一的实例。 2. **工厂模式**:定义一个用于创建对象的接口,让子类决定实例化...
5. 单例模式(Singleton Pattern):单例模式确保一个类只有一个实例,并提供全局访问点。通常用于管理资源或者需要全局唯一的对象,如线程池、数据库连接等。 【结构型设计模式】 6. 适配器模式(Adapter Pattern...
1. 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 2. 行为型模式,共十一种:命令模式、迭代器模式、观察者模式、备忘录模式、状态模式、模板方法模式、策略模式、责任链模式...
1. **单例模式**:确保一个类只有一个实例,并提供一个全局访问点。在AS3中,单例模式常用于管理共享资源或配置信息,如Singleton.as示例。 2. **工厂模式**:提供一个接口来创建对象,但让子类决定实例化哪一个类...