`

单例模式中的多线程分析

阅读更多

谈到单例模式,我们立马会想到饿汉式和懒汉式加载,所谓饿汉式就是在创建类时就创建好了实例,懒汉式在获取实例时才去创建实例,即延迟加载。

饿汉式:

package com.bijian.study;

public class Singleton {

	private Singleton() {
	}

	// 注意这是private 只供内部调用
	private static Singleton instance = new Singleton();

	// 这里提供了一个供外部访问本class的静态方法,可以直接访问  
	public static Singleton getInstance() {
		return instance;
	}
}

懒汉式:

package com.bijian.study;

public class Singleton {

	private static Singleton instance = null;

	public static synchronized Singleton getInstance() {

		// 这个方法比上面有所改进,不用每次都进行生成对象,只是第一次     
		// 使用时生成实例,提高了效率!
		if (instance == null)
			instance = new Singleton();
		return instance;
	}
}

     上面第二中形式的lazy initialization,也就是说第一次调用时初始Singleton,以后就不用再生成了。

注意到lazy initialization形式中的synchronized,这个synchronized很重要,如果没有synchronized,那么使用getInstance()是有可能得到多个Singleton实例。一般认为第一种形式要更加安全些。

       对于上面的懒汉式方式,从多线程角度来看,Synchronized放到方法上会影响性能。于是我们不难想到将其放到方法里。

package com.bijian.study;

public class Singleton {

	private static Singleton instance = null;
	private static String lock = new String();

	public static Singleton getInstance() {

		// 这个方法比上面有所改进,不用每次都进行生成对象,只是第一次     
		// 使用时生成实例,提高了效率!
		if (instance == null)
			synchronized(lock) {
				instance = new Singleton();
			}
		return instance;
	}
}

我们稍加分析,不难发现,这样会存在线程安全问题,假如线程一刚好执行完if (instance == null)的判断语句(还未加锁),调度至线程二也执行这条判断语句,也是true,也进入了if的语句块中,这样就会产生两个实例,而非单实例了。

此时,我们稍加分析,那还不容易,在锁里面再加一个是否为空的判断,即所谓的double-checked locking (DCL),如下所示:

package com.bijian.study;

public class Singleton {

	private static Singleton instance = null;
	private static String lock = new String();

	public static Singleton getInstance() {

		// 这个方法比上面有所改进,不用每次都进行生成对象,只是第一次     
		// 使用时生成实例,提高了效率!
		if (instance == null)
			synchronized(lock) {
				if (instance == null) {
					instance = new Singleton();
				}
			}
		return instance;
	}
} 

     此时,很多人都会觉得无懈可击了。从多线程角度来看,这样写的单例确实没有问题了,但从Java的类创建原理来看,可能还有问题。从浅显简单的理解来看,就是对象还未完全创建出来,但instance变量已被赋值,此时另一个线程获取实例时,会得到instance,但它的堆空间及相关的方法还未完成时,调用实例方法就会出错。

       啊?还存在这样的问题呀?这可以虚拟机的实现问题,难道还要我考虑?是的,其实稍加改动,就可以避免这样的问题。

       解决办法,加一个局部变量,如下所示:

package com.bijian.study;

public class Singleton {

	private static Singleton instance = null;
	private static String lock = new String();

	public static Singleton getInstance() {

		// 这个方法比上面有所改进,不用每次都进行生成对象,只是第一次     
		// 使用时生成实例,提高了效率!
		if (instance == null)
			synchronized(lock) {
				if (instance == null) {
					Singleton temp = new Singleton();
					instance = temp;
				}
			}
		return instance;
	}
}

 

进一步深入可参考:

Double-checked locking and the Singleton pattern

When is a singleton not a singleton?

分享到:
评论

相关推荐

    c++单例模式线程日志类

    在这个特定的场景中,我们讨论的是一个实现了单例模式的日志类,该类专为多线程环境设计,具备日志等级控制、精确的时间戳以及可变长参数和标准格式化输出的功能。 首先,让我们深入了解单例模式。单例模式的主要...

    线程安全的单例模式

    为了确保单例模式在多线程环境中的正确性,需要考虑如何使其具有线程安全性。 1. **同步方法**: ```java public final class ThreadSafeSingleton { private static ThreadSafeSingleton singObj = null; ...

    设计模式之单例模式(结合工厂模式)

    单例模式是软件设计模式中的一种经典模式,它保证了类只有一个实例存在,并提供一个全局访问点。在Java等面向对象编程语言中,单例模式常用于管理共享资源,如数据库连接池、线程池或者配置文件等。结合工厂模式,...

    单例模式详解 1. 什么是单例模式? 1.1 单例模式的核心要素 1.2 为什么需要单例模式? 2. 单例模式的实现方式 2.1 饿汉式(静态常量) 2.2 饿汉式(静态代码块) 2.3 懒汉式(线程

    7.2 测试多线程环境 8. 实用单例模式的最佳实践 8.1 何时使用单例 8.2 如何选择实现方式 8.3 单例的替代方案 9. 实际案例分析 9.1 Spring中的单例 9.2 Java Runtime类 10. 总结 11. 练习 11.1 练习答案 11.1.1 线程...

    Qt两种方法实现多线程并安全结束线程及QMutex加锁Qt单例化实现

    综上,理解并熟练运用QThread、QMutex和Qt的单例模式对于开发高效的多线程Qt应用至关重要。实践中要注重线程间的通信和同步,确保程序的稳定性和数据一致性。同时,单例模式的应用有助于管理全局资源,减少内存开销...

    单例模式和多例模式

    单例模式和多例模式是软件设计模式中的两种重要类型,它们主要用来控制类的实例化过程,确保在系统中某一类只有一个实例或者多个实例。 单例模式是一种限制类的实例化过程,使得一个类在整个应用程序中只能有一个...

    单例模式,single

    特别是在现代软件开发中,考虑到多线程环境、类加载机制以及序列化等因素的影响,合理地设计和实现单例模式变得尤为重要。通过上述讨论,我们可以看到单例模式在不同场景下的适用性及其潜在问题,并学习到了相应的...

    单例模式应用场景

    多线程环境下的线程池** 在线程密集型应用中,频繁创建和销毁线程会带来较大的开销。线程池技术通过预创建一定数量的线程,供后续任务复用来解决这一问题。单例模式在这一场景下的应用能够保证线程池本身是全局...

    设计模式——策略模式 & 单例模式

    策略模式和单例模式是软件设计中两种非常重要的设计模式,它们在实际开发中有着广泛的应用。在这篇文章中,我们将深入探讨这两种模式的核心概念、实现方式以及如何在实际项目中运用。 策略模式是一种行为设计模式,...

    单例模式(用winform写的)

    通过分析这个示例代码,我们可以学习如何将单例模式融入到实际项目中,了解如何创建和使用单例类,以及如何在Winform的环境中处理多线程问题。 单例模式的优点包括: 1. 节省内存:避免了多次实例化同一对象。 2. ...

    单例模式详解

    在复杂的系统架构中,还需要考虑多线程、多类加载器、跨JVM等特殊场景下的单例模式实现。 通过本文的介绍,我们可以看到单例模式不仅限于一个简单的类实现,还可以根据不同的应用场景进行扩展和优化。在软件开发...

    Java多线程-解决单例模式中的懒汉式的线程安全问题

    ### Java多线程—解决单例模式中的懒汉式的线程安全问题 #### 一、单例设计模式的线程安全问题 ##### (1)饿汉式没有线程安全问题 **饿汉式**是一种非常典型的单例模式实现方式,其特点是在类加载时就完成了实例...

    线程相关的单例模式

    在软件开发中,单例模式是一种常用的面向对象...在压缩包文件"test"中,可能包含了实现这些线程相关单例模式的代码示例,通过阅读和分析这些代码,我们可以深入理解各种单例模式的实现细节及其在多线程环境下的行为。

    几种单例模式说明(并有实例)

    在多线程环境下,单例模式可以避免对资源的重复加载,降低系统的内存开销,提高系统性能。单例模式通常分为懒汉式、饿汉式、登记式等几种类型。 ### 懒汉式单例模式 懒汉式单例模式是最简单的单例模式之一,它的...

    使用Java单例模式实现一个简单的日志记录器.txt

    单例模式是一种常用的软件设计模式,在该模式中,一个类只能创建一个实例,并且提供了一个全局访问点来访问该实例。单例模式的主要优点包括减少系统资源的消耗、简化系统配置、提供统一的访问点等。 #### 二、Java...

    Java单例模式.pdf

    由于synchronized的存在,这个懒汉式单例模式在多线程环境下会存在性能问题,因为每次调用getInstance方法时都需要获取和释放锁。 接下来,我们看饿汉式单例模式的代码。EagerSingleton类展示了一个饿汉式单例模式...

    java 设计模式 mvc模式 单例模式 代理 工厂 简单工厂

    这些方法都是为了保证在多线程环境下,单例对象的唯一性与实例化过程的安全性。 代理模式是另一种经常被采用的设计模式。在代理模式中,一个类代表另一个类的功能。这种模式常用于控制对某个对象的访问,以增强原...

    通过C++实现一个设计模式-单例模式.zip

    懒汉式单例模式则是在第一次被引用时才进行初始化,这种方式节省了资源,但是需要处理多线程访问同步问题。线程安全的懒汉式单例模式通常使用互斥锁(mutex)来控制对单例实例创建的访问,从而保证线程安全,但是会...

Global site tag (gtag.js) - Google Analytics