单件模式,也称单例模式,用以创建独一无二的、只能有一个实例的对象。

  单件模式的类图是所有模式的类图中最简单的——只有一个类。尽管从类设计的视角来看单件模式很简单,但是实现上还是会遇到一些问题,本文着重对这一点来进行分析解决。

  最简单的单件模式的实现,代码如下:

 /**
* Created by McBye King on 2016/10/23.
*/
public class Singleton {
private static Singleton singleton;
private Singleton(){}
public static Singleton getSingleton(){
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}

  结合以上的代码,对单件模式进行简单的阐述。

  单件模式中,利用一个静态变量来记录Singleton类的唯一实例。把构造器声明为私有的,只有自Singleton类内才可以调用构造器。为了实例化这个类,于是调用getSingleton方法,在其中实例化并返回这个实例。这里还有一个“延迟实例化”的思想,在此处,如果我们不需要这个实例,它就永远不会产生。当然在实际中可以给这个类添加其他行为。

  看起来这已经是单件模式的全部了,因为单件模式太简单了,但是如果细细追究,还有很多问题。

  想一个问题,如果有两个或者更多的线程调用使用上述的单例的类,会怎么样呢?

  因为没有对这个单例的类对外的接口getSingleton()方法进行保护,每一个线程都可以同时调用到这个函数,有可能若干个线程同时访问到这个方法,同时进行了if(singleton == null)的判断,因为是同时的,所以大家看到的都是未曾实例化的singleton,于是紧接着就有若干个Singleton实例对象出现——这完全违反了单件模式的本意。——如果你说这也太偶然了吧,确实是的,但是确实实际存在的问题,有很大几率出现重大bug的问题

  接下来我们考虑怎么解决这个问题。

  1、只要把getSingleton()变成同步(synchronized)方法,多线程灾难几乎就可以解决了,如下示例:

/**
* Created by McBye King on 2016/10/23.
*/
public class Singleton {
private static Singleton singleton;
private Singleton(){}
public static synchronized Singleton getSingleton(){
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}

  通过添加synchronized关键字到getSingleton()方法中,我们迫使每个线程在进入这个方法之前,要先等候别的线程离开该方法。也就是说不允许两个线程可以同时进入这个方法。

  2、添加synchronized方法无疑可以解决同步问题,但是很明显会降低性能,这又导致了另一个问题。如果可以牺牲性能,也即getSingleton()方法的性能对应用程序影响不大的时候,就用上面的方法没有错。否则,就把“延迟实例化”变成“急切”创建实例把。

/**
* Created by McBye King on 2016/10/23.
*/
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton(){}
public static synchronized Singleton getSingleton(){
return singleton;
}
}

  在静态初始化器中创建单件,就保证了线程安全。当然了,这种办法适用于应用程序总是创建并使用单件实例,或者在创建和运行时方面的负担不会太重。

  3、相对更好一点的办法是:用“双重检查加锁”,在getSingleton()中减少使用同步

  来看看代码:

/**
* Created by McBye King on 2016/10/23.
*/
public class Singleton {
private volatile static Singleton singleton;
private Singleton(){}
public static Singleton getSingleton(){
if(singleton == null){
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}

  在上述代码的getSingleton()实例化方法中,先检查实例,如果不存在,就进入同步区块;且只有第一次才会彻底执行同步区块中的代码。

  其中的volatile关键字确保:当singleton变量被初始化成Singleton实例时,多个线程正确地处理singleton变量。

  如果性能是考虑的重点的话,上述办法可以帮助大大减少getSingleton()的时间耗费。——前提是在Java 5以及之后的Java版本中。

  4,、今天再更新一种方法,结合以上的三种方法的优点,既能拥有单件模式延迟实例化的优点,又能保证性能的要求,同时也避免了多线程情况下出错。

  具体代码如下:

/**
* Created by McBye King on 2016/10/23.
*/
public class Singleton {
private static Singleton singleton;
private Singleton(){}
private static synchorized void init(){
if(singleton == null){
singleton = new Singleton();
}
}
//延迟实例化
public static Singleton getSingleton(){
if(singleton == null){
init();
}
return singleton;
}
}

  如上代码所示,只有第一次实例化Singleton的时候才会调用init()方法。

5、今天再更新一种方法,使用内部类的形式,只有在第一次需要单例实例的时候才会初始化该内部类,从而实现只加载一次该实例,同时也保证线程安全。

publicclassSingleton{
// 使用内部类实现延迟加载
privatestaticclassSingletonHolder{
privatestaticSingletonsingleton=newSingleton();
}

publicstaticSingletongetSingleton() {
returnSingletonHolder.singleton;
}
}

  这几天在蚂蚁金服的技术面中考察到了这种方法,很巧妙的实现了以上几种方法的优点,也避免了其缺点。

  以上代码的github地址:AntiTechInterview

  原文地址:深度解析Java单例模式

最新文章

  1. Designing IP-Based Video Conferencing Systems: Dealing with Lip Synchronization(唇音同步)
  2. jquery_选择器
  3. Shared pool
  4. oracle的启动过程(不分模式启动)
  5. ThinkPHP 学习笔记 ( 三 ) 数据库操作之数据表模型和基础模型 ( Model )
  6. 两篇很牛的vim使用技巧
  7. [摘] SQLPLUS Syntax
  8. 使用程序获取整型数据和浮点型数据在内存中的表示---gyy整理
  9. mybatis以序列周期,同样处理的这个问题的价值
  10. c语言复杂声明解析
  11. codeforces #275 div2题解
  12. 中了J.Fla的毒!
  13. 29.使用register_chrdev_region()系列来注册字符设备
  14. 【转载】Linux cgroup资源隔离各个击破之 - cpu隔离1
  15. 【BZOJ2428】均分数据(模拟退火)
  16. 神州数码OSPF路由汇总配置
  17. 云笔记项目-网页端debug功能学习
  18. retrofit框架接口调用时候报Throwing new exception
  19. 动手动脑(lesson 3)
  20. KCF的弊端

热门文章

  1. iOS 数据存储之SQLite3的使用
  2. Python学习基础
  3. 使用Masstransit开发基于消息传递的分布式应用
  4. 基于 SailingEase WinForm Framework 开发优秀的客户端应用程序(1:概述)
  5. Spring Boot -- 配置切换指南
  6. Win10 字体模糊解决(DPI缩放禁用),设置默认输入法英文
  7. MongoDB安装与故障
  8. Kafka 文档用例
  9. 了解HTML图像
  10. 用spm2构建seajs项目的过程