设计模式5-单例模式(Singleton)解析+案例实践+总结

本文是对面向对象设计模式--单例模式(Singleton)的解析,主要分为定义解析、经典单例模式、多线程环境下的单例模式、多案例练习加深对单例模式的理解、最后总结知识要点。

第一篇:定义解析

单例模式是GoF四人帮整理的《设计模式-可复用面向对象软件基础》一书中23种设计模式中归类为创建型模式中的设计模式,23种设计模式根据它们的用途分为三大类:5种创建型模式、7种结构型模式、11种行为型模式。

引自GoF四人帮对单例模式(Singleton)的定义与类图:

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

保证一个类仅有一个实例并提供一个访问它的全局访问点,通过定义可以清晰的知道单例模式的目的,保证一个类在系统中只能有一个实例对象,这意味着不能让任何其它类可以直接通过new来实例化一个新的实例对象。提供一个访问它的全局访问点,即提供一个获取单例对象的方法,可以在系统其它地方通过全局访问点获取这个单例对象。

 

第二篇:经典的单例模式

单例模式用来创建独一无二的,只能有一个实例的对象。有一些对象在系统中只需要一个,如线程池、缓存和注册表等,这些类对象只能有一个实例,如果制造出多个实例,就会导致许多问题发生,如程序的行为异常、不一致的结果等。

为了防止类对象被多次创建,单例对象的构建方法必须是私有的。而且需要有一个静态方法,通过类来调用这个方法获取实例对象。

单例对象通常用来管理共享的资源。

经典的单例模式实现:

public class Singleton{
  private static Singleton uniqueInstance;
  private Singleton(){}
  public static Stingleton getInstance(){
  if(uniqueInstance == null){
    uniqueInstance = new Singleton();
  }
  return uniqueInstance;
  }
}

1)通过一个静态变量uniqueInstance来记录Singleton的惟一实例。

2)把构建器声明为私有,只有在Singleton内部才能调用。

3)通过全局访问方法getInstance()实例化对象,并返回对象实例。

第三篇:多线程环境下的单例模式

如图所示,在多线程的情况下经典的单例模式实现会出现问题,创建了多个实例对象。

在多线程环境下,保证单例类只有一个对象方法:

1)使用synchronize将getInstance()变成同步方法

public class Singleton{
  private static Singleton uniqueInstance;
  private Singleton(){}
  public static synchronize Stingleton getInstance(){
  if(uniqueInstance == null){
    uniqueInstance = new Singleton();
  }
  return uniqueInstance;
  }
}

使用synchronize将方法变成同步方法,保证每次只有一个线程执行getInstance()方法,这样就保证了只会有一次new实例化对象,从而保证了多线程环境下,只会创建一个对象。但是通过分析,其实我们只有第一次调用getInstance()方法的时候才需要同步,一旦实例化对象之后就不需要再同步,但使用此方法,每一次调用都会进行同步,当频繁调用的时候,会严重拖累系统的性能。

2)使用静态初始化器创建单例对象,也称急切的单例模式。

public class Singleton{
  private static Singleton uniqueInstance = new Singleton();
  private Singleton(){}
  public static Stingleton getInstance(){
    return uniqueInstance;
  }
}

通过静态初始化器创建单例对象,保证了线程案例。利用这种做法,我们依赖JVM加载这个类时马上创建惟一实例对象。JVM保证任何线程在访问uniqueInstance对象之前一定先创建此实例。此种方法也种急切的单例模式,在系统加载类时便创建单例对象,会增加初始化时资源消耗。

注意:如果存在两个类加载器以上的情况,每个类加载器可能会创建各自的单例。每个类加载器都定义一个命名空间,如果有两个以上的类加载器,不同的类加载器可能会加载同一个类,从而从程序上看一个类被加载了多次,解决办法:自行指定类加载器,并指定同一个类加载器。JVM1.2之前垃圾回收器可能会吃掉单例对象,需要建立单例注册表修复。

3)使用双重检查加锁的方式

public class Singleton{
  private static volatile Singleton uniqueInstance;
  private Singleton(){}
  public static Stingleton getInstance(){
  if(uniqueInstance == null){
   synchronize(Singleton.class){
    if(uniqueInstance == null){
      uniqueInstance = new Singleton();
    }
   }
  }
  return uniqueInstance;
 }
}

利用双重检查加锁的方式,只有第一次调用getInstance()创建单例对象时会进行同步,从而保证多线程环境下,只创建一个实例对象。当单例对象创建完成后,再调用getInstance()方法将不会进行同步,采用此方法,提高了性能,大大减少了getInstance()的损耗。

uniqueInstance使用volatile声明,保证单例对象引用在多线程环境下对各线程都是可见的,一旦创建单例对象完成,所有线程都将知晓对象已创建,uniqueInstance不再为null,从而保证双重检查加锁的方式能够正确保证只会创建一个对象。

注意:JDK1.5之前的版本,因为volatile的实现会导致双重加锁失效。故不能采用此实现。

 

第四篇:案例实践

练习案例源码:https://github.com/chentian114/100-dayaction/tree/master/designpattern-1

ClassicSingletonDemo经典的单例模式案例:

在多线程环境下会出错。

StaticSingletonDemo急切的单例模式案例:

在静态加载类时创建对象

SynchronizedSingletonDemo同步的单例模式案例:

直接在获取单例的方法上通过synchronized同步来获取单例,在大量调用的情况下,性能会越来越差。

ThreadSingletonDemo双重检查加锁的方式实现单例案例:

通过使用双重检查与volatile与局部使用synchronized,在实现线程安全地创建单例情况下不会对性能有太大的影响。只有第一次创建单例的时间需要同步。被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。由于volatile关键字可能会屏蔽掉虚拟机中一些必要的代码优化,所以运行效率并不是很高。因此一般,没有特别需要,不要使用。

EnumSingletonDemo使用枚举类型实现单例模式案例:

使用枚举实现单例非常简单,只需要编写一个包含单个元素的枚举类型即可。使用枚举来实现单例控制会更加简洁,而且提供了序列化机制,并由JVM提供保障防止多次实例化。

 

第五篇:总结

单例模式确保程序中一个类最多只有一个实例对象。单例模式提供了访问这个实例的全局访问点。实现单例模式一般需要私有化构建方法,一个静态变量和一个静态方法。

单例模式的优点:1)减少内存开支2)减少创建对象所需系统的性能开销3)避免资源的多重占用。

单例模式的缺点:1)扩展困难,只能直接对代码进行修改。

单例模式的使用场景:1)要求生成唯一序列号的环境2)整个项目中只需要一个共享访问点3)创建一个对象需要消耗的资源过多。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值