Java 设计模式 (1) 单例模式

本贴最后更新于 2919 天前,其中的信息可能已经事过景迁

概述

单例模式是 23 种设计模式之一。顾名思义,即在应用环境中一个类只能创建一个对象实例,这样处理是因为有些场景如:线程池、缓存、日志对象、打印机对象等只能有一个实例,若有多个,则会产生程序、资源等等问题。单例模式的简要定义为:**确保一个类只有一个实例,并提供一个全局访问点。 **

注意:

1. 以下讨论均在一个类加载器、一个 JVM 前提下;
2. 以下讨论不涉及反射机制。

方法一:延迟实例化方式(非线程安全)

单例模式的实现方式很简单,即将构造器设为私有(private),不允许其他类通过 new 方式创建对象,并对外提供一个返回该类实例的接口。具体代码如下:

public class Singleton{  
  private Singleton(){} 
  //将构造器设为私有 
  private static Singleton singleton; 
  public Singleton getSingleton(){  
      if(singleton==null){  
         singleton=new Singleton();  
	  }  return singleton;  
  } 
} 

该类将构造器设为 private,则只有该类内部可以通过 new 创建实例,同时对外提供 getSingleton()方法,调用该方法返回一个实例。(若实例未创建,则创建,创建后始终返回该实例) 此种方式在需要该类实例时才会创建可以称之为“延迟实例化“的方式(也称为“懒汉模式“)。另外重要的是,此种方法是非线程安全的。 试想若有 2 个线程同时到达 if(singleton==null)的判断中,并且都得到 true,则就会产生两个实例,破坏单例模式的约束。

方法二:双重检查加锁

若想在多线程环境中应用单例模式,还需要对刚才的方法加以改变,将创建的实例看做共享的资源,当需要创建资源时加锁控制访问,操作完成后释放锁。


public class Singleton{  
  private Singleton(){}  
  private volatile static Singleton singleton;  
  public static Singleton getSingleton(){  
       if(singleton==null){  //检查实例,若未创建进入同步块  
	     synchronized(Singleton.class){ 
	       //同步块开始  
	        if(singleton==null){ //进入同步块后再检查一次,防止出现2个线程同时通过第一次检查的情况(当时还未同步)  
	          singleton= new Singleton();  
		    } 
	      } //同步块结束  
	    } 
		return singleton; 
  }  
} 

volatile 关键字的含义,简单来说即:告诉 JVM,对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。 singleton= new Singleton()这句话不是原子操作,其主要包含 3 步:

1. 给 Singleton 分配空间;
2. 初始化 Singleton 的构造函数;
3. 将分配的空间地址返回给实例。

java 编译器允许处理器乱序执行以提高执行效率,因此在实际执行中可能是 1-2-3,也可能是 1-3-2。若是 1-3-2,若当线程 1 执行完 3 后轮到线程 2 执行,此时线程 1 由于执行了 3,singleton 已不为空,而未执行构造函数等操作,因此得到的为不完整的实例,线程 2 使用该实例时就会出错。 另外需要注意的是:双重检查加锁方法必须是 JDK1.5 或以后版本才支持

方法三:初始实例化(“饿汉模式”)

以上使用线程加锁的方式可以解决多线程的方式,不过有些麻烦,还要考虑性能的一些影响。JVM 可以保证一个类只被加载一次,因此可以考虑下面的方法


public class Singleton{ 

  private Singleton(){} 
  
  private static Singleton singleton = new Singleton();  
  public Singleton getSingleton(){  
     if(singleton==null){  
	    singleton = new Singleton();  
      }  
	  return singleton;  
	  }  
  }

此种方法在类被初次记载时即初始化了该类实例,不存在线程安全问题。但如果该类创建需配置文件时该方法就无能为力了。

参考文献
1、《Head First 设计模式》

2、 http://crud0906.iteye.com/blog/576321

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3187 引用 • 8213 回帖
  • 设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    200 引用 • 120 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • hucker

    最后一句话是什么意思 如果该类创建需配置文件时。。。

  • 其他回帖
  • erhuojiuri

    double check