多线程下的singleton
YI GJ ---2010年5月5日
单例模式是最简单的设计模式之一,但是对于java开发者来说,它却有很多的缺陷。下面讨论单例模式在面对多线程时,如果处理缺陷。
单例模式的用意在于前一段中所关心的。通过单例模式你可以:
确保一个类只有一个实例被建立 ,
提供了一个对对象的全局访问指针 ,
下面看个单例经典例子:
public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
instance = new Singleton();//懒汉式单例
}
return instance;
}
}
在例1中的单例模式的实现很容易理解。Singleton类保持了一个对单独的单例实例的静态引用,并且从静态方法getInstance()中返回那个引用。
关于Singleton类,有几个让我们感兴趣的地方。首先,Singleton使用了一个众所周知的懒汉式实例化去创建那个单例类的引用;结果,这个单例类的实例直到getInstance()方法被第一次调用时才被创建。这种技巧可以确保单例类的实例只有在需要时才被建立出来。其次,注意Singleton实现了一个private的构造方法,这样客户端不能直接实例化一个Singleton类的实例。
在单线程情况下,对该类添加main方法进行测试:
public static void main(String[] agrs) {
for (int i = 0; i < 5; i++) {
System.out.println("Singleton.getInstance().hashCode() --"+ i +" -- "+Singleton.getInstance().hashCode());
}
}
测试结果如下:
可以看出在单线程情况下,调用多次 getInstance方法,得到的Singleton的实例的hash码都是一样的。说明只生成了一个实例对象。
但是我们仔细的分析代码 ,会发现其实前面的代码不是线程安全的,如果两个线程,我们称它们为线程1和线程2,在同一时间调用Singleton.getInstance()方法,如果线程1先进入if块,然后线程2进行控制,那么就会有Singleton的两个的实例被创建。尽管这个问题就在这段代码上:
if(instance==null){
instance = new Singleton();//懒汉式单例
}
如果一个线程在第二行的赋值语句发生之前切换,那么成员变量instance仍然是null,然后另一个线程可能接下来进入到if块中。在这种情况下,两个不同的单例类实例就被创建。不幸的是这种假定很少发生,这样这种假定也很难在测试期间出现。下面我们来模拟这个问题的产生。
public class Singleton {
private static Singleton instance = null;
private static boolean flag = true;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
threadSleep();
instance = new Singleton();// 懒汉式单例
}
System.out.println("instance.hashCode() : "+instance.hashCode());
return instance;
}
public static void main(String[] agrs) {
new Thread(new SingletonTestRunnable()).start();
new Thread(new SingletonTestRunnable()).start();
}
private static void threadSleep(){
if(flag){
try {
System.out.println("当前线程休眠5秒!");
Thread.currentThread().sleep(5000);
System.out.println("休眠5秒结束!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class SingletonTestRunnable implements Runnable {
public void run() {
Singleton s = Singleton.getInstance();
}
}
在Singleton中加入一个方法threadSleep(),让当前线程休眠5秒钟,SingletonTestRunnable实现Runnable接口,在run方法中 调用Singleton.getInstance()得到Singleton的实例,运行main方法,测试结果如下:
可以看出对于两个线程得到的Singleton的实例的hashCode是不同的,则可以知道—这是两个不同的实例。分析代码:在第一个线程运行到
if (instance == null) {
threadSleep();
instance = new Singleton();// 懒汉式单例
}
时候休眠5秒钟,随后第二线程进入该代码片段,运行到threadSleep方法时候,也休眠5秒钟,第一个线程醒来,继续向下执行代码 instance = new Singleton(),创建第一个Singleton对象实例,随后第二个线程也执行了 instance = new Singleton(),这样创建了第二个实例对象。
那么我们可以看出,Songleton 不是线程安全的。
尝试修正:
public static Singleton getInstence() {
if (instence == null) {
methodSleep();
synchronized (Singleton .class) {
if (instence == null) {
instence = new Singleton (); //双重检查确保在并发的条件下也是只有一个实例存在。但是由于使用了
return instence; //synchronized关键字,在第一次访问时效率比较慢。。
}
}
}
return instence;
}
第一种方案:在getInstance()方法前面加上 关键字 synchronized ,测试结果如下:
分析这个结果:开启两个线程,只打印了一次测试汉字,这就说明 只调用了一次 threadSleep()方法,而打印出来的hashCode也是相同的,表示引用了同一个Singleton对象实例。
第二种方案:一个简单、快速而又是线程安全的单例模式实现
public class Singleton {
public static Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static void main(String[] agrs) {
new Thread(new SingletonTestRunnable()).start();
new Thread(new SingletonTestRunnable()).start();
}
}
class SingletonTestRunnable implements Runnable {
public void run() {
System.out.println( Singleton.INSTANCE.hashCode());
}
}
测试结果看出两个线程创建的Singleton对象引用指向是同一块堆内存,hashCode相同。
这段代码是线程安全的是因为静态成员变量一定会在类被第一次访问时被创建。你得到了一个自动使用了懒汉式实例化的线程安全的实现。
分享到:
相关推荐
前言 前段时间在网上看到了个的面试题,大概...本文主要将从基本的单线程中的Singleton开始,慢慢讲述多线程与Singleton的那些事。 单线程 在单线程下,下面这个是常见的写法: template class Singleton
在Java编程中,Singleton模式是一种常用的设计模式,它确保一个类只有一个实例,并提供一个全局访问点。...正确实现Singleton模式,可以提高程序的稳定性和可维护性,避免多线程环境下的资源竞争和实例重复创建问题。
.doc 格式 详细解析多线程技术 基础篇 • 怎样创建一个线程 • 受托管的线程与 Windows线程 • 前台线程与后台线程 • 名为BeginXXX和EndXXX的方法是做什么用的 • 异步和多线程有什么关联 WinForm多线程编程...
- 在多线程环境下,我们需要确保多个线程访问Singleton的`Instance`属性时不会创建多个实例。这里使用了`volatile`关键字来解决内存可见性和指令重排序问题,确保所有线程都能看到最新的 `_instance` 值。 - `lock...
这是一个关于多线程下的单例模式优化代码。public class Singleton { private static Singleton instance; private Singleton (){ } public static Singleton getInstance(){ //对获取实例的方法进行同步 if...
总结起来,多线程环境下的单例模式实现需要注意线程安全问题,尤其是懒汉式单例,需要采取适当的同步措施来防止多线程环境下的实例化问题。此外,对于不同场景的需求,可以选择不同的实现方式来优化性能和资源使用。
- **双重检查锁定Singleton**:确保单例模式的安全初始化,但某些情况下可能存在线程竞争的问题。 - **比较锁语句**:`lock`、`Mutex`、`Event`各有适用场景,选择取决于同步需求的复杂性和粒度。 5. **Web和IIS*...
在多线程环境中实现Singleton时需要注意线程安全性。 一种简单的线程安全Singleton实现方法是使用双重检查锁定(double-checked locking)技术: ```cpp class Singleton { private: static Singleton* instance; ...
此外,Singleton模式虽然简单易用,但也有一些缺点,如违背了开闭原则(对扩展开放,对修改关闭),导致程序难以进行单元测试,以及在某些多线程环境下可能引发性能问题。在设计时需权衡其适用性。
通过这种方式,即使在多线程环境下也能确保Singleton对象的唯一性,同时避免了不必要的同步开销。 #### 六、总结 在C++中实现Singleton模式时,需要注意的关键点包括但不限于资源管理、线程安全以及如何有效地减少...
- **Singleton的双锁实现**:双锁(double-checked locking)模式用于确保单例的安全创建,但可能存在并发问题,因此在多线程环境下应谨慎使用。 ### Web和IIS - **应用程序池**、**WebApplication**和线程池的关系...
在多线程环境中,实现线程安全的Singleton至关重要,因为不正确的实现可能导致多个实例的创建,违背了Singleton的基本原则。 1. 寂寞的Singleton Singleton通常采用静态内部类、枚举或懒汉式(Lazy Initialization...
在Linux环境下,线程安全的Singleton模板是一种至关重要的设计模式,尤其在多线程编程中。Singleton模式的主要目的是确保一个类只有一个实例,并提供一个全局访问点。这种模式在系统资源管理、缓存、日志记录等方面...
多线程情况下对该singleton对象创建操作的串行化,没有对singleton对象访问的操作进行串行化。 这个包就是修正这个问题的。只不过访问方式要从 CMyclass::instance().DoSomething() 改成 CMyclass::instance()->...
C# 多线程讲解的基础概念 多线程开发的基础知识点包括:创建线程、线程池、同步和异步、WinForm 多线程编程、线程安全、锁定机制等。 一、创建线程 创建线程有多种方式,包括使用 Thread 类、Delegate....
在多线程环境中,如果没有适当的同步机制,`getInstance()`可能会在不同线程中同时创建多个实例。在C++11及更高版本中,我们可以使用`std::call_once`和`std::once_flag`来确保实例只在第一次调用时创建: ```cpp #...
并且由于实例在类加载时就创建,所以不存在多线程下的性能问题。 缺点:Singleton实例在类加载时就被创建,即使可能永远都不会用到,造成了内存浪费。 ### 2. 懒汉式(Synchronized Method) 这种方式是在`...