`
sunway
  • 浏览: 115151 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

单例模式在多线程下的使用

阅读更多

昨天去Nokia研发中心面试,出了很多关于设计模式的题,总共10道题有4道里面有设计模式吧,足以见到他们对设计模式的重视。在提问环节中也被问到了设计模式,我们以前使用设计模式往往不够深入,下面我转了别人关于单例模式的论述,也是这次面试中我没回答出来的问题。

转自http://hi.baidu.com/rtsbtx/blog/item/331681f88162cc05d9f9fde8.html

 

单例模式

 

概要
单例模式是最简单的设计模式之一,但是对于Java的开发者来说,它却有很多缺陷。在本月的专栏中,David Geary探讨了单例模式以及在面对多线程(multithreading)、类装载器(classloaders)和序列化(serialization)时如何处理这些缺陷。

单例模式适合于一个类只有一个实例的情况,比如窗口管理器,打印缓冲池和文件系统,它们都是原型的例子。典型的情况是,那些对象的类型被遍及一个软件系统的不同对象访问,因此需要一个全局的访问指针,这便是众所周知的单例模式的应用。当然这只有在你确信你不再需要任何多于一个的实例的情况下。
单例模式的用意在于前一段中所关心的。通过单例模式你可以:


确保一个类只有一个实例被建立
提供了一个对对象的全局访问指针
在不影响单例类的客户端的情况下允许将来有多个实例

尽管单例设计模式如在下面的图中的所显示的一样是最简单的设计模式,但对于粗心的Java开发者来说却呈现出许多缺陷。这篇文章讨论了单例模式并揭示了那些缺陷。
注意:你可以从Resources下载这篇文章的源代码。

单例模式

在《设计模式》一书中,作者这样来叙述单例模式的:确保一个类只有一个实例并提供一个对它的全局访问指针。
下图说明了单例模式的类图。
(图1)


单例模式的类图

正如你在上图中所看到的,这不是单例模式的完整部分。此图中单例类保持了一个对唯一的单例实例的静态引用,并且会从静态getInstance()方法中返回对那个实例的引用。
例1显示了一个经典的单例模式的实现。
例1.经典的单例模式

Java代码 复制代码
  1. public class ClassicSingleton {   
  2.    private static ClassicSingleton instance = null;   
  3.      
  4.    protected ClassicSingleton() {   
  5.       // Exists only to defeat instantiation.   
  6.     }   
  7.    public static ClassicSingleton getInstance() {   
  8.       if(instance == null) {   
  9.           instance = new ClassicSingleton();   
  10.        }   
  11.       return instance;   
  12.     }   
  13. }   



在例1中的单例模式的实现很容易理解。ClassicSingleton类保持了一个对单独的单例实例的静态引用,并且从静态方法getInstance()中返回那个引用。
关于ClassicSingleton类,有几个让我们感兴趣的地方。首先,ClassicSingleton使用了一个众所周知的懒汉式实例化去创建那个单例类的引用;结果,这个单例类的实例直到getInstance()方法被第一次调用时才被创建。这种技巧可以确保单例类的实例只有在需要时才被建立出来。其次,注意ClassicSingleton实现了一个protected的构造方法,这样客户端不能直接实例化一个ClassicSingleton类的实例。然而,你会惊奇的发现下面的代码完全合法:

public class SingletonInstantiator {      
  public SingletonInstantiator() {      
    ClassicSingleton instance = ClassicSingleton.getInstance();    
ClassicSingleton anotherInstance =    
new ClassicSingleton();    
        ...      
   }      
}    

 

前面这个代码片段为何能在没有继承ClassicSingleton并且ClassicSingleton类的构造方法是protected的情况下创建其实例?答案是protected的构造方法可以被其子类以及在同一个包中的其它类调用。因为ClassicSingleton和SingletonInstantiator位于相同的包(缺省的包),所以SingletonInstantiator方法能创建ClasicSingleton的实例。
这种情况下有两种解决方案:一是你可以使ClassicSingleton的构造方法变化私有的(private)这样只有ClassicSingleton的方法能调用它;然而这也意味着ClassicSingleton不能有子类。有时这是一种很合意的解决方法,如果确实如此,那声明你的单例类为final是一个好主意,这样意图明确,并且让编译器去使用一些性能优化选项。另一种解决方法是把你的单例类放到一个外在的包中,以便在其它包中的类(包括缺省的包)无法实例化一个单例类。
关于ClassicSingleton的第三点感兴趣的地方是,如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。
第四点,如果ClasicSingleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。
最后也许是最重要的一点,就是例1中的ClassicSingleton类不是线程安全的。如果两个线程,我们称它们为线程1和线程2,在同一时间调用ClassicSingleton.getInstance()方法,如果线程1先进入if块,然后线程2进行控制,那么就会有ClassicSingleton的两个的实例被创建。

正如你从前面的讨论中所看到的,尽管单例模式是最简单的设计模式之一,在Java中实现它也是决非想象的那么简单。这篇文章接下来会揭示Java规范对单例模式进行的考虑,但是首先让我们近水楼台的看看你如何才能测试你的单例类。

测试单例模式

接下来,我使用与log4j相对应的JUnit来测试单例类,它会贯穿在这篇文章余下的部分。如果你对JUnit或log4j不很熟悉,请参考相关资源。

例2是一个用JUnit测试例1的单例模式的案例:
例2.一个单例模式的案例
import org.apache.log4j.Logger;    
import junit.framework.Assert;    
import junit.framework.TestCase;    
      
public class SingletonTest extends TestCase {    
   private ClassicSingleton sone = null, stwo = null;    
   private static Logger logger = Logger.getRootLogger();    
      
   public SingletonTest(String name) {    
      super(name);    
    }    
   public void setUp() {    
       logger.info("getting singleton...");    
       sone = ClassicSingleton.getInstance();    
       logger.info("...got singleton: " + sone);    
      
       logger.info("getting singleton...");    
       stwo = ClassicSingleton.getInstance();    
       logger.info("...got singleton: " + stwo);    
    }    
   public void testUnique() {    
       logger.info("checking singletons for equality");    
       Assert.assertEquals(true, sone == stwo);    
    }    
}    
 


例2两次调用ClassicSingleton.getInstance(),并且把返回的引用存储在成员变量中。方法testUnique()会检查这些引用看它们是否相同。例3是这个测试案例的输出:
例3.是这个测试案例的输出

Buildfile: build.xml    
      
init:    
      [echo] Build 20030414 (14-04-2003 03:08)    
      
compile:    
      
run-test-text:    
      [java] .INFO main: [b]getting singleton...[/b]    
      [java] INFO main: [b]created singleton:[/b] Singleton@e86f41    
      [java] INFO main: ...got singleton: Singleton@e86f41    
      [java] INFO main: [b]getting singleton...[/b]    
      [java] INFO main: ...got singleton: Singleton@e86f41    
      [java] INFO main: checking singletons for equality    
      
      [java] Time: 0.032    
      
      [java] OK (1 test)   
 正如前面的清单所示,例2的简单测试顺利通过----通过ClassicSingleton.getInstance()获得的两个单例类的引用确实相同;然而,你要知道这些引用是在单线程中得到的。下面的部分着重于用多线程测试单例类。
分享到:
评论
1 楼 masuweng 2011-06-18  
今天培训恰好讲到这些内容了

相关推荐

    使用C++11实现线程安全的单例模式

    线程安全的单例模式在多线程环境下尤其重要,因为不正确的实现可能导致多个线程创建多个实例,这违反了单例模式的基本原则。C++11引入了新的特性,如std::mutex和std::call_once,使得实现线程安全的单例模式变得...

    C++ 多线程和多线程下的单例模式

    本资源描述了C++11 中多线程的创建,C++11中std命名空间中将boost库中的Thread加入,boost多线程从准标准变为标准,其中还介绍了C++ 多线程下的单例模式的使用,本文档为txt文档

    多线程单例模式并发访问

    总结起来,多线程环境下的单例模式实现需要注意线程安全问题,尤其是懒汉式单例,需要采取适当的同步措施来防止多线程环境下的实例化问题。此外,对于不同场景的需求,可以选择不同的实现方式来优化性能和资源使用。

    43丨单例模式(下):如何设计实现一个集群环境下的分布式单例模式?1

    传统的单例模式在多进程或多节点的分布式环境中不再适用,因为每个进程或节点都可以独立创建自己的单例实例。要实现分布式环境下的单例,可以采用以下策略: 1. **数据库锁**:在创建单例对象之前,所有节点尝试...

    使用单例模式实现计数器

    因为在多线程环境下,如果多个线程同时访问`Increment()`方法,可能会导致计数不准确。为了确保线程安全,我们可以使用`lock`关键字或者`Interlocked`类: ```csharp public void Increment() { lock (_instance) ...

    Qt多工程调用单例实现,带线程锁

    在本文中,我们将深入探讨如何在Qt环境中使用多工程调用单例模式,并结合线程锁来确保数据管理的一致性...通过学习这个案例,开发者可以掌握如何在Qt中正确设计和使用单例模式,以及如何在多线程环境下保证数据一致性。

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

    通过运行这个示例,开发者可以了解如何配置日志等级、如何在多线程环境下安全地使用日志以及如何利用可变长参数和格式化输出功能。 总之,这个C++实现的单例模式日志类结合了多种高级特性,包括多线程支持、日志...

    QT单例模式、多线程、双重校验加锁

    在QT中,单例模式、多线程以及双重校验加锁(Double-Checked Locking)是常见的编程模式和技术,尤其在处理并发和资源管理时显得尤为重要。 **单例模式** 是一种设计模式,确保一个类只有一个实例,并提供一个全局...

    线程安全的单例模式

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

    浅议单例模式之线程安全(转)

    在多线程环境下,线程安全的单例模式尤为重要,因为如果不正确实现,可能会导致多个线程同时创建多个实例,违反了单例模式的基本原则。 在Java中,单例模式通常有以下几种实现方式: 1. 饿汉式(静态常量): ...

    Java多线程实战之单例模式与多线程的实例详解

    延迟加载/懒汉模式是在调用方法时实例才被创建,在多线程环境下,会出现取出多个实例的情况,与单例模式的初衷是相背离的。如下代码所示: public class MyObject { private static MyObject myObject; private ...

    单例模式下,使用多线程实现

    在多线程环境下,单例模式的实现需要特别考虑线程安全问题。因为多个线程可能会同时尝试创建单例对象,如果没有正确的同步控制,就可能导致多个实例的产生,违反了单例模式的设计初衷。以下是一些在多线程环境中实现...

    多线程并发下的单例模式-源码

    该资源是多线程并发下的单例模式-源码,几乎包含了所有方式实现的单例模式,并且能够确保在多线程并发下的线程安全性。 读者可结合本人博客 http://blog.csdn.net/cselmu9?viewmode=list 中的《线程并发之单例模式...

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

    在Java中,为了确保多线程环境下的正确性,我们可以使用synchronized关键字来保证同步,但这会引入性能开销。DCL模式通过在实例化单例时使用 volatile 关键字和双层检查,解决了这个问题。volatile关键字保证了多...

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

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

    C#单例模式详解 C#单例模式详解C#单例模式详解

    在C#中,单例模式常用于管理共享资源或控制类的实例化过程,以提高性能、节约系统资源,特别是在整个应用程序生命周期内只需要一个对象的情况下。 首先,实现C#单例模式通常有几种常见方法: 1. 饿汉式(静态常量...

    设计模式——单例模式

    这里的 `getInstance()` 方法使用了 `synchronized` 关键字进行同步,保证了在多线程环境下的线程安全。但是,同步操作会降低性能,因为每次调用都需要进行锁的获取和释放。 为了解决懒汉式单例模式的性能问题,有...

    使用单例模式创建学生管理系统(饿汉式、懒汉式)

    但是,如果不进行同步控制,懒汉式在多线程环境下可能会创建多个实例,因此通常采用双重检查锁定(Double-Checked Locking,DCL)来实现线程安全的懒汉式单例: ```java public class Singleton { private ...

    多线程下的单例模式优化

    这是一个关于多线程下的单例模式优化代码。public class Singleton { private static Singleton instance; private Singleton (){ } public static Singleton getInstance(){ //对获取实例的方法进行同步 if...

Global site tag (gtag.js) - Google Analytics