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

Java 单例模式解析

 
阅读更多
所谓单例模式,简单来说,就是在整个应用中保证只有一个类的实例存在。就像是Java Web中的application,也就是提供了一个全局变量,用处相当广泛,比如保存全局数据,实现全局性的操作等。

  1. 最简单的实现

  首先,能够想到的最简单的实现是,把类的构造函数写成private的,从而保证别的类不能实例化此类,然后在类中提供一个静态的实例并能够返回给使用者。这样,使用者就可以通过这个引用使用到这个类的实例了。



public class SingletonClass {


  private static final SingletonClass instance = new SingletonClass();


  public static SingletonClass getInstance() {
      return instance;
  }


  private SingletonClass() {
  }
  }



  如上例,外部使用者如果需要使用SingletonClass的实例,只能通过getInstance()方法,并且它的构造方法是private的,这样就保证了只能有一个对象存在。

  2. 性能优化——lazy loaded

  上面的代码虽然简单,但是有一个问题——无论这个类是否被使用,都会创建一个instance对象。如果这个创建过程很耗时,比如需要连接10000次数据库,并且这个类还并不一定会被使用,那么这个创建过程就是无用的。怎么办呢?

  为了解决这个问题,想到了新的解决方案:



public class SingletonClass {


  private static SingletonClass instance = null;


  public static SingletonClass getInstance() {


      if(instance == null) {


          instance = new SingletonClass();


      }


      return instance;
  }
  private SingletonClass() {
  }
  }



  代码的变化有两处——首先,把instance初始化为null,直到第一次使用的时候通过判断是否为null来创建对象。因为创建过程不在声明处,所以那个final的修饰必须去掉。

  我们来想象一下这个过程。要使用SingletonClass,调用getInstance()方法。第一次的时候发现instance是null,然后就新建一个对象,返回出去;第二次再使用的时候,因为这个instance是static的,所以已经不是null了,因此不会再创建对象,直接将其返回。

  这个过程就成为懒汉模式,也就是迟加载——直到使用的时候才进行加载。

  3. 同步

  上面的代码很清楚,也很简单。然而就像那句名言:“80%的错误都是由20%代码优化引起的”。单线程下,这段代码没有什么问题,可是如果是多线程,麻烦就来了。我们来分析一下:

  线程A希望使用SingletonClass,调用getInstance()方法。因为是第一次调用,A就发现instance是null的,于是它开始创建实例,就在这个时候,CPU发生时间片切换,线程B开始执行,它要使用SingletonClass,调用getInstance()方法,同样检测到instance是null——注意,这是在A检测完之后切换的,也就是说A并没有来得及创建对象——因此B开始创建。B创建完成后,切换到A继续执行,因为它已经检测完了,所以A不会再检测一遍,它会直接创建对象。这样,线程A和B各自拥有一个SingletonClass的对象——单例失败!

  解决的方法也很简单,那就是加锁:



     public class SingletonClass {
  private static SingletonClass instance = null;
  public synchronized static SingletonClass getInstance() { //加上线程锁
   if(instance == null) {
     instance = new SingletonClass();
   }
    return instance;
  }
  private SingletonClass() {
  }
  }


  是要getInstance()加上同步锁,一个线程必须等待另外一个线程创建完成后才能使用这个方法,这就保证了单例的唯一性。

  4. 又是性能

  上面的代码又是很清楚很简单的,然而,简单的东西往往不够理想。这段代码毫无疑问存在性能的问题——synchronized修饰的同步块可是要比一般的代码段慢上几倍的!如果存在很多次getInstance()的调用,那性能问题就不得不考虑了!

  让我们来分析一下,究竟是整个方法都必须加锁,还是仅仅其中某一句加锁就足够了?我们为什么要加锁呢?分析一下出现lazy loaded的那种情形的原因。原因就是检测null的操作和创建对象的操作分离了。如果这两个操作能够原子地进行,那么单例就已经保证了。于是,我们开始修改代码:



     public class SingletonClass {
  private static SingletonClass instance = null;
  public static SingletonClass getInstance() {
  synchronized (SingletonClass.class) {
  if(instance == null) {
  instance = new SingletonClass();
  }
  }
  return instance;
  }
  private SingletonClass() {
  }
  }


  首先去掉getInstance()的同步操作,然后把同步锁加载if语句上。但是这样的修改起不到任何作用:因为每次调用getInstance()的时候必然要同步,性能问题还是存在。如果……如果我们事先判断一下是不是为null再去同步呢?



 public class SingletonClass {
  private static SingletonClass instance = null;
  public static SingletonClass getInstance() {
  if (instance == null) {
  synchronized (SingletonClass.class) {
  if (instance == null) {
  instance = new SingletonClass();
  }
  }
  }
  return instance;
  }
  private SingletonClass() {
  }
  }


  还有问题吗?首先判断instance是不是为null,如果为null,加锁初始化;如果不为null,直接返回instance。

  这就是double-checked locking设计实现单例模式。到此为止,一切都很完美。我们用一种很聪明的方式实现了单例模式。

  5. 解决方案

  说了这么多,难道单例没有办法在Java中实现吗?其实不然!

  在JDK 5之后,Java使用了新的内存模型。volatile关键字有了明确的语义——在JDK1.5之前,volatile是个关键字,但是并没有明确的规定其用途——被volatile修饰的写变量不能和之前的读写代码调整,读变量不能和之后的读写代码调整!因此,只要我们简单的把instance加上volatile关键字就可以了。



public class SingletonClass {
  private volatile static SingletonClass instance = null;
  public static SingletonClass getInstance() {
  if (instance == null) {
   synchronized (SingletonClass.class) {
      if(instance == null) {
      instance = new SingletonClass();
      }
   }
  }
  return instance;
  }
  private SingletonClass() {
  }
  }


  然而,这只是JDK1.5之后的Java的解决方案,那之前版本呢?其实,还有另外的一种解决方案,并不会受到Java版本的影响:



public class SingletonClass {
  private static class SingletonClassInstance {
   private static final SingletonClass instance = new SingletonClass();
  }
  public static SingletonClass getInstance() {
  return SingletonClassInstance.instance;
  }
  private SingletonClass() {
  }
  }


  在这一版本的单例模式实现代码中,我们使用了Java的静态内部类。这一技术是被JVM明确说明了的,因此不存在任何二义性。在这段代码中,因为SingletonClass没有static的属性,因此并不会被初始化。直到调用getInstance()的时候,会首先加载SingletonClassInstance类,这个类有一个static的SingletonClass实例,因此需要调用SingletonClass的构造方法,然后getInstance()将把这个内部类的instance返回给使用者。由于这个instance是static的,因此并不会构造多次。

  由于SingletonClassInstance是私有静态内部类,所以不会被其他类知道,同样,static语义也要求不会有多个实例存在。并且,JSL规范定义,类的构造必须是原子性的,非并发的,因此不需要加同步块。同样,由于这个构造是并发的,所以getInstance()也并不需要加同步。

  至此,已经完整的了解了单例模式在Java语言中的时候,提出了两种解决方案。个人偏向于第二种,并且Effiective Java也推荐的这种方式
分享到:
评论

相关推荐

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

    ### 使用Java单例模式实现一个简单的日志记录器 #### 一、单例模式简介 单例模式是一种常用的软件设计模式,在该模式中,一个类只能创建一个实例,并且提供了一个全局访问点来访问该实例。单例模式的主要优点包括...

    PHP 单例模式解析和实战

    ### PHP 单例模式解析和实战 #### 一、什么是单例模式? 单例模式是一种常用的软件设计模式,尤其在面向对象编程中被广泛应用。它确保一个类只有一个实例,并且提供了一个全局访问点来获取该实例。单例模式在很多...

    单例模式单例模式单例模式

    单例模式是软件开发中最基本的设计模式之一,尤其在Java编程中应用广泛。它的核心思想是确保一个类仅有一个实例,并提供一个全局访问点来获取该实例。这种模式特别适用于那些只需要一个实例就可以满足系统需求的情况...

    java单例模式开发的7种写法

    ### Java单例模式开发的七种写法 #### 概述 单例模式是一种常用的软件设计模式,其目的是确保一个类仅有一个实例,并提供一个全局访问点。在Java编程语言中,实现单例模式的方法有很多种,不同的实现方式具有不同的...

    java单例模式的登录界面

    根据提供的信息,我们可以总结出以下相关的IT知识点: ...这些知识点不仅涵盖了单例模式的应用,还包括了数据库连接管理和Swing登录界面的设计等方面,对于理解Java开发中的常用技术具有一定的参考价值。

    java设计模式之单例模式详解

    Java设计模式之单例模式详解 一、单例模式概览 单例模式(Singleton Pattern)是面向对象设计模式中的一种,属于创建型模式。它确保一个类仅有一个实例,并提供一个全局访问点来访问该实例。单例模式的核心在于控制...

    JAVA设计模式中的单例模式

    ### JAVA设计模式中的单例模式解析 在软件工程与编程领域,设计模式是解决特定问题的一套被广泛接受的解决方案。其中,单例模式(Singleton Pattern)是一种常用的创建型模式,其核心在于确保一个类只有一个实例,...

    设计模式——单例模式(懒汉模式)

    本文将详细探讨单例模式的懒汉式实现,并结合源码进行解析。 首先,我们要理解单例模式的基本原则:限制类的实例化次数,确保一个类只有一个实例,并提供一个全局访问点。懒汉式单例模式的特点是延迟加载,即只有在...

    JAVA单例模式

    ### JAVA单例模式详解 #### 一、单例模式简介 单例模式是设计模式中最简单的一种模式,其目的是确保某一个类只有一个实例存在,并且该类能够自行创建这个实例。这种模式通常用于那些需要频繁实例化然后销毁的对象...

    实例解析Java单例模式编程中对抽象工厂模式的运用

    总结来说,将Java单例模式与抽象工厂模式相结合,可以提供一种灵活且可控的方式来创建和管理一组相关的产品,这在大型系统中尤其有用,能够适应不断变化的需求和配置。通过这样的设计,我们能够实现模块间的解耦,...

    java模式设计之单例模式

    【单例模式详解】 单例模式是设计模式中的一种,它的核心...以上是对单例模式的详细解析,它在Java编程中有着广泛的应用,特别是在需要全局唯一对象的场景中。理解并合理运用单例模式,可以提高代码的可维护性和性能。

    Java设计模式单例模式(Singleton)用法解析

    Java设计模式单例模式(Singleton)用法解析 java设计模式单例模式(Singleton)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局反访问点。单例模式是创建型模式。单例模式在生活中应用也很广泛,...

    实验12 单例模式与枚举.doc

    本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)...进阶篇有反射、泛型、注解、网络编程、多线程、序列化、数据库、Servlet、JSP、XML解析、单例模式与枚举。本专栏主要为Java入门者提供实验参考。

    Singleton 单例模式的介绍以及解析

    单例模式是软件设计模式中的一种经典模式,它在Java、C++、Python等众多编程语言中被广泛应用。单例模式的主要目的是确保一个类只有一个实例,并提供全局访问点,这样可以有效地控制资源的共享,避免因为多次实例化...

    java设计模式之单例模式解析

    单例模式是软件设计模式中的一种基础模式,它确保一个类只有一个实例,并提供一个全局访问点。这种模式在Java编程中被广泛使用,特别是在需要控制资源的场景下,如数据库连接池、缓存管理等。单例模式的主要优点在于...

    经典工厂单例模式典范

    总结来说,"经典工厂单例模式典范"是一个将工厂模式和单例模式相结合的经典实例,它在Java、Web和.NET开发中有着广泛的应用。通过学习和理解这个模式,开发者可以更好地组织代码,提高代码的复用性和可维护性,同时...

    全面解析Java设计模式之单例模式

    【Java 单例模式】详解 单例模式是设计模式中的一种基础模式,它确保一个类在任何情况下只有一个实例存在。这种模式的核心在于限制类的实例化,保证全局范围内只有一个对象,同时也提供了一个全局访问点,使得其他...

    Java实现单例设计模式方法解析

    Java实现单例设计模式方法解析 单例设计模式是软件设计模式中的一种,旨在确保一个类只有一个实例,并提供一个全局访问点。Java中有多种方式可以实现单例设计模式,本文将对这些方法进行解析,并提供示例代码。 一...

    单例设计模式源码和案例解析

    在Java编程中,单例模式的应用广泛,例如控制资源的共享、管理配置信息等。本篇将深入解析单例设计模式的四种实现方式,并通过具体的案例进行详细分析。 一、懒汉式(线程不安全) 懒汉式单例的特点是在类被加载时...

    Java之23种设计模式解析

    本资源“Java之23种设计模式解析”源自尚硅谷教育机构,由宋红康老师主讲的“玩转Java”系列课程。这份资料详细介绍了在Java编程中常用的23种设计模式,旨在提升开发者对于软件设计的理解和应用能力,从而写出更加...

Global site tag (gtag.js) - Google Analytics