单例模式在编程中很常见。当多个线程使用时,必须使用某种类型的同步。为了使你的代码更高效,Java程序员在单例模式中使用双重检测锁定来限制并发执行的代码量。但是,由于Java内存模型的一些鲜为人知的细节,这种双重检查锁定是不能保证工作,它会偶尔失败。此外,其失败的原因并不明显,涉及到Java内存模型的内部细节。并且是很难追查的。在本文的其余部分,我们将仔细研究双重检查锁定了解它什么时候就发生了。
单例模式的习惯写法
Listing1
import java.util.*;
class Singleton
{
private static Singleton instance;
private Vector v;
private boolean inUse;
private Singleton()
{
v = new Vector();
v.addElement(new Object());
inUse = true;
}
public static Singleton getInstance()
{
if (instance == null) //1
instance = new Singleton(); //2
return instance; //3
}
}
这个类保证了Singleton实例只能被创建一次,是靠将构造方法的访问权限设为私有的,getInstance()方法只能创建一个Singleton对象。这种设计在单线程中是很好的。但是在多线程环境下,你必须通过加锁机制来保护getInstance()方法,否则会创建出两个不同的实例对象。假设现在有两个不同的线程来访问getInstance()方法,可能会按照下面的顺序来执行:
1.线程1调用getInstance()方法,在1处判断结果为true
2.线程1进入if语句块中,但是在执行2处的代码时,开始阻塞了,被线程2赶超
3.线程2调用getInstance()方法,在1处判断结果也为true
4.线程2进入if语句块中,继续执行2处的代码,创建了一个实例,并让instance变量指向了该对象。
5.线程2返回了Singleton对象在第3行中的代码
6.线程1开始继续执行,从被打断的地方
7.线程1在2处又创建了一个实例对象,并返回
上面导致创建了两个实例对象,而该设计模式的目的是只创建一个实例对象。这个问题可以通过用synchronized修饰方法来解决,如下所示:Listing2
public static synchronized Singleton getInstance()
{
if (instance == null) //1
instance = new Singleton(); //2
return instance; //3
}
代码经过这么改进确实解决了上面的问题。通过代码的仔细分析,会发现,每次调用getInstance()方法,都会进行加锁,这显然是不合理的,除了第一次在创建实例的时候需要锁的保护,后来的调用是不再需要的。锁机制是会影响效率的。
为了提高代码执行的效率,双重检查锁定方法被引入了。它的目的就是为了解决一个问题就是除了第一次需要锁保护外,其它是不需要的。
代码改进如下所示:Listing3
public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
Listing3中的代码存在Listing1的同样的问题,多个线程可以同时进入if语句块。例如:当线程1正要创建实例的时候被阻塞了,这时线程2仍然能进入if语句块中,这肯定就会再次创建一个实例出来。因此,我们需要再次去check是否为空。代码如下:Listing4
public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class) { //1
if (instance == null) //2
instance = new Singleton(); //3
}
}
return instance;
}
在理论上双重检查保证了不会再次创建实例,但是在实际中却不是这样的。原因是在JVM中,被称为“out-of-order writes”是导致问题的主要原因。
当在执行3处的代码时,可能发生这样的一种情况,还没有初始化完毕,就已经赋给了instance,这时另一个线程判断instance时就不为空了,就会返回一个部分初始化的实例对象。代码做了如下改进:Listing5
public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class) { //1
Singleton inst = instance; //2
if (inst == null)
{
synchronized(Singleton.class) { //3
inst = new Singleton(); //4
}
instance = inst; //5
}
}
}
return instance;
}
改进的地方就是将构造方法的调用和赋值分开来写,只在调用构造方法中加锁,就是new的时候。后续参见
http://www.ibm.com/developerworks/java/library/j-dcl/index.html
分享到:
相关推荐
为了保证线程安全,可以使用双重检查锁定模式(Double-Checked Locking Pattern),确保只有一个实例被创建。但是这要求必须使用关键字volatile修饰静态实例变量,以防止指令重排序导致的问题。单例模式1和单例模式2...
单例模式(Singleton Pattern)是软件设计模式中的一种,它保证一个类只有一个实例,并提供一个全局访问点。这种模式在很多场景下非常有用,比如控制资源的唯一性、全局配置对象或者缓存服务等。本篇文章将深入探讨...
单例模式(Singleton Pattern)是软件开发中最常用的创建型设计模式之一,它的主要目标是确保一个类只有一个实例,并提供一个全局访问点。单例模式在很多场景下都非常有用,例如系统配置管理、日志记录等场合,这些...
考虑到线程安全,现代Java中通常采用双重检查锁定(Double-Checked Locking)或静态内部类等方式来实现线程安全的单例。 在"adv-java-factory-singleton"项目中,我们可以期待看到如何在Java中实现和应用这些模式的...
单例模式(Singleton Pattern)是一种常用的软件设计模式,它确保一个类仅有一个实例,并提供一个全局访问点。这种模式通常用于那些需要频繁实例化然后销毁的对象,或者创建对象需要消耗大量资源的情况,比如读取...
单例模式(Singleton Pattern)是一种常用的软件设计模式,属于创建型模式之一。其主要目的是确保一个类只有一个实例,并提供一个全局访问点来访问该实例。这在某些情况下非常有用,比如系统中需要频繁创建后又销毁...
- 实现方法:通常使用同步方法或双重检查锁定(Double-Checked Locking)来保证线程安全。 - 优点:延迟实例化,节省资源。 - 缺点:每次获取实例时都要进行同步操作,效率较低。 2. **饿汉式**: - 特点:在类...
一种解决方案是使用双检锁机制(Double Checked Locking Pattern)来确保线程安全。 5. **资源管理**:单例模式下的对象生命周期往往较长,因此需要注意资源的管理,避免内存泄漏等问题。 #### 五、应用场景 1. **...
#### 五、双重检查锁定(Double-Checked Locking)单例模式 为了解决懒汉式单例模式在多线程环境下的性能问题,可以采用双重检查锁定策略。这种方法只在必要时进行同步,提高了效率。 ```java public class ...
单件模式(Singleton Pattern)是软件设计模式中的一种基础模式,它确保一个类只有一个实例,并提供一个全局访问点。在C#中,单例模式的实现有多种方式,包括懒汉式、饿汉式以及线程安全的实现。下面我们将详细讨论...
- 可以通过双重检查锁定(Double-Checked Locking)优化性能。 **线程安全问题及解决方案** - **问题**:在多线程环境中,若不采取措施,则可能会导致单例模式失效,即生成多个实例。 - **解决方案**: - 使用`...
首先,单例模式(Singleton Pattern)是设计模式中最简单的一种。它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。单例模式在很多应用场合中都有应用,如在需要进行全局控制,或者共享资源访问时。 ...
为了解决这一问题,可以采用双重检查锁定(Double-Checked Locking)模式来确保线程安全性: ```cpp class Singleton { public: static Singleton* Instance() { if (_instance == nullptr) { lock_guard<mutex>...
在软件设计模式中,单例模式(Singleton Pattern)是一种广泛应用且至关重要的模式。它确保一个类只有一个实例,并提供一个全局访问点,使得任何地方都能方便地获取这个唯一实例。在Delphi应用开发中,正确理解和...
单件模式(Singleton Pattern)是设计模式中的一种结构型模式,它的主要目的是确保一个类只有一个实例,并提供一个全局访问点。这种模式常用于系统中需要频繁创建和销毁的对象,例如日志服务、线程池、数据库连接等...
在C++中实现单例,需要注意线程安全问题,可能会使用到静态成员变量、双重检查锁定(Double-Checked Locking)以及C++11的std::call_once和std::once_flag等工具来确保正确性。 第5章可能会讲解“工厂模式”...
单例模式(Singleton Pattern)是设计模式中的一种,其主要意图是限制类的实例化次数,确保一个类只有一个实例,并提供一个全局访问点。这样的设计可以避免过多的实例化导致的资源浪费,特别是在需要全局共享资源...
线程安全的单例模式通常通过双重检查锁定(double-checked locking)或枚举实现。例如,`java.lang.Runtime`就是单例模式的实例。 2. 工厂模式(Factory pattern):工厂模式提供了一种创建对象的方式,将对象的...
单例模式需要注意线程安全问题,如双重检查锁定(Double-Checked Locking)等实现方式。 - **抽象工厂(Abstract Factory)**:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。 - **建造者...
标题"防止程序多次运行"涉及到的关键技术是单例模式(Singleton Pattern)和互斥锁(Mutex)。下面我们将深入探讨这两个概念及其在C#中的实现。 **单例模式** 单例模式是一种设计模式,它限制一个类只能有一个实例...