饿汉式单例类
public class Singleton
{
private Singleton(){
}
private static Singleton instance = new Singleton();
private static Singleton getInstance(){
return instance;
}
}
饿汉式提前实例化,没有懒汉式中多线程问题,但不管我们是不是调用getInstance()都会存在一个实例在内存中
内部类式单例类
public class Singleton
{
private Singleton(){
}
private class SingletonHoledr(){
private static Singleton instance = new Singleton();
}
private static Singleton getInstance(){
return SingletonHoledr.instance;
}
}
内部类式中,实现了延迟加载,只有我们调用了getInstance(),才会创建唯一的实例到内存中.并且也解决了懒汉式中多线程的问题.解决的方式是利用了Classloader的特性.
懒汉式单例类
public class Singleton
{
private Singleton(){
}
private static Singleton instance;
public static Singleton getInstance(){
if(instance == null){
return instance = new Singleton();
}else{
return instance;
}
}
}
在懒汉式中,有线程A和B,当线程A运行到第8行时,跳到线程B,当B也运行到8行时,两个线程的instance都为空,这样就会生成两个实例。解决的办法是同步:
public class Singleton
{
private Singleton(){
}
private static Singleton instance;
public static synchronized Singleton getInstance(){
if(instance == null){
return instance = new Singleton();
}else{
return instance;
}
}
}
这样写程序不会出错,因为整个getInstance是一个整体的"critical section",但就是效率很不好,因为我们的目的其实只是在第一个初始化instance的时候需要locking(加锁),而后面取用instance的时候,根本不需要线程同步。
于是聪明的人们想出了下面的做法:
public class Singleton
{
private Singleton(){
}
private static Singleton instance;
public static Singleton getInstance(){
if(instance == null){
synchronized(this){
if(instance == null)
return instance = new Singleton();
}
}else{
return instance;
}
}
}
思路很简单,就是我们只需要同步(synchronize)初始化instance的那部分代码从而使代码既正确又很有效率。
这就是所谓的“双检锁”机制(顾名思义)。
很可惜,这样的写法在很多平台和优化编译器上是错误的。
原因在于:instance = new Singleton()这行代码在不同编译器上的行为是无法预知的。一个优化编译器可以合法地如下实现instance = new Singleton():
1. instance = 给新的实体分配内存
2. 调用Singleton的构造函数来初始化instance的成员变量
现在想象一下有线程A和B在调用getInstance,线程A先进入,在执行到步骤1的时候被踢出了cpu。然后线程B进入,B看到的是instance 已经不是null了(内存已经分配),于是它开始放心地使用instance,但这个是错误的,因为在这一时刻,instance的成员变量还都是缺省值,A还没有来得及执行步骤2来完成instance的初始化。
当然编译器也可以这样实现:
1. temp = 分配内存
2. 调用temp的构造函数
3. instance = temp
如果编译器的行为是这样的话我们似乎就没有问题了,但事实却不是那么简单,因为我们无法知道某个编译器具体是怎么做的,因为在Java的memory model里对这个问题没有定义。
双检锁对于基础类型(比如int)适用。很显然吧,因为基础类型没有调用构造函数这一步。
分享到:
相关推荐
《C++ and the Perils of Double Checked Locking》是一篇探讨C++编程中双重检查锁定(Double-Checked Locking)模式潜在问题的文献。在多线程编程中,双重检查锁定是一种常见的优化策略,旨在减少对同步原语的依赖...
3. 双重检查锁定(Double-Checked Locking):结合了前两者,延迟初始化并保证线程安全。 ```java public class Singleton { private volatile static Singleton instance; private Singleton() {} public static...
为了解决这个问题,可以采用双重检查锁定(Double-Checked Locking)的策略: ```cpp class Singleton { private: Singleton() {} static Singleton* instance; static std::mutex mtx; public: static ...
这种方式在Java中需要注意线程安全问题,例如双重检查锁定(Double-Checked Locking)。 - **饿汉式**:类加载时就立即创建实例,保证线程安全,但可能会浪费内存。 - **静态内部类**:利用Java类加载机制保证线程...
// 双检锁/双重校验锁(DCL,即 double-checked locking) public class Singleton { private volatile static Singleton instance; private Singleton() {} public static Singleton getInstance() { if ...
1. **双检锁/双重校验锁(DCL,即 double-checked locking)** ```java public class Singleton { private volatile static Singleton instance; private Singleton() {} public static Singleton getInstance...
3. **双检单例(Double-Checked Locking)**: 双检锁模式试图在保证线程安全的同时减少不必要的同步开销。其核心思想是只有在实例为空时才进行同步操作: ```csharp public class Singleton { private static ...
在这个版本中,我们进行了双重检查锁定(Double-Checked Locking),先检查`singleton`是否为空,如果为空才进行同步操作。这种优化减少了同步的开销,但仍然保证了线程安全。 4. **静态内部类**: ```java ...
- DCL双检锁:解决了经典懒汉式的问题,线程安全但代码复杂。 - 静态内部类:利用类装载机制保证单例,线程安全且推荐使用。 **案例小结** 选择单例模式的实现方式应根据实际需求,考虑性能、线程安全和代码简洁性...
2. **双检锁/双重校验锁定(Double-Checked Locking,DCL)** 这种方法在早期的.NET框架中被广泛使用,但后来由于.NET内存模型的变化,其线程安全性变得复杂。在.NET 4.0及更高版本中,可以安全地使用此方法: ``...
懒汉式的常见实现是使用双重检查锁定(Double-Checked Locking,DCL): ```java public class Singleton { private volatile static Singleton instance; private Singleton() {} public static Singleton ...
但是,如果不进行同步控制,懒汉式在多线程环境下可能会创建多个实例,因此通常采用双重检查锁定(Double-Checked Locking,DCL)来实现线程安全的懒汉式单例: ```java public class Singleton { private ...
2. **双重检查锁定(DCL,Double-Checked Locking)**:这是一种优化的懒汉式实现,通过添加volatile关键字和synchronized关键字来保证线程安全。在多线程环境下,只有一个线程能执行初始化操作,其他线程会等待初始...
另一个解决方案是使用双重检查锁定机制(Double-Checked Locking)来实现线程安全的单例模式。这种方法可以减少 synchronize 的使用,从而提高系统的性能。 ```java public class Singleton { private volatile ...
2. **双重检查锁定(Double-Checked Locking)**:这种方法是在多线程环境下保证线程安全的单例。首先检查是否已经实例化,如果未实例化,则同步锁定构造函数,确保只有一个线程可以进入,然后创建实例。这种方式...
为了解决这个问题,可以使用“双检锁/双重校验锁”(Double-Checked Locking)策略: ```java public class ThreadSafeSingleton { private volatile static ThreadSafeSingleton instance; private ...
除了静态内部类方式,还有其他实现单例的常见方法,如双重检查锁定(Double-Checked Locking,DCL)和枚举方式。但考虑到Android环境的特殊性,静态内部类的方式更推荐,因为它既简单又高效,同时也避免了线程安全...
在Java中,通常通过私有构造器、静态内部类或双重检查锁定(Double-Checked Locking)等方式实现单例。以静态内部类为例,代码可能如下: ```java public class Singleton { private static class SingletonHolder...
或者采用双重检查锁定(Double-Checked Locking)的方式: ```java public static SingleTon getInstance() { if (singleTon == null) { synchronized (SingleTon.class) { if (singleTon == null) { singleTon...
2. **使用双检锁(Double-Checked Locking)**:为了在多线程环境下保证线程安全,可以采用双检锁机制。这避免了每次调用`getInstance()`时都需要加锁,提高了效率。不过,需要注意的是,这种方法在某些旧版本的C++...