单例(Singleton)模式:

 
保证一个类在系统里只能有一个对象被实例化。
 
如:缓存池、数据库连接池、线程池、一些应用服务实例等。
 
难点:在多线程环境中,保证实例的唯一性。
 
 

最简单的单例模式:

  1. 保证该类构造方法是私有的,外部无法创建该类型的对象;
  2. 提供一个全局访问点,方便给客户对象提供对此单例对象的使用;
 

public class Singleton {
/**
* 私有变量,外界无法访问
* 可以定义 public 类型 instance变量,把属性直接暴露给客户对象,则没必要实现getInstance()方法
* 但是可读性降低,而且直接暴露实例变量的名字给客户程序,会增加代码的耦合度
*/
private static Singleton instance = new Singleton(); static {
//...
} // 唯一的 private构造方法,客户对象无法创建该对象实例
private Singleton() { } // 全局访问点
public static Singleton getInstance() {
return instance;
}
} // 客户使用单例模式代码
Singleton singleton = Singleton.getInstance();
    如果该实例需要比较复杂的初始化过程时,把这个过程应该写在 static{ ... }代码快中。
    注意:此实现是线程安全的,当对个线程同时去访问该类的 getInstance( ) 方法时,不会初始化多个不同的对象,这是因为,JVM 在加载此类时,对于 static 属性的初始化只能由一个线程执行且仅一次。
 
 

进阶:

    Statci 在加载类时就会被初始化,出于性能等方面的考虑,我们希望延迟实例化单例对象,只有在第一次使用该类的实例时才去实例化。
 
延迟创建
 

public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
    我们把单例的实例化过程移至 getInstance( )方法,而不是在加载类时预先创建,当访问此方法时,首先判断该实例是不是已经被实例化过了,如果已被初始化,则直接返回这个对象的引用;否则,创建这个实例并初始化,最后返回这个对象引用。
 
    使用 if (instance == null) 判断是否实例化完成了,此方法不是线程安全的。
 
 
线程安全
    在高并发的环境中,getInstance( ) 方法将返回多个指向不同的该类实例。
 
  Thread 1 Thread 2
1 if (instance == null)  
2   if (instance == null)
3 Singleton instance = new Singleton();  
4   Singleton instance = new Singleton();
5 return instance;  
6   return instance;
 
    在时刻 1和 2,由于还没有创建单例对象,Thread 1 和 Thread 2都会进入创建单例实例的代码块分别创建实例。在时刻 3 ,Thread 1创建了一个实例对象,但是 Thread 2此时已无法知道,于是继续创建一个新的实例对象,导致这两个线程持有的实例并非为同一个。
    更为糟糕的是,在没有自动内存回收机制的语言平台上运行这样的单例模式,如:C++,以为我们认为创建了一个单例实例,忽略了其他线程所产生的对象,不会手动去回收它们,从而引起内存泄漏。
 
为了解决这个问题,我们给次方法添加 关键字,代码如下:
 
  

public   static   synchronized  Singleton getInstance() {
if ( instance == null ) {
instance = new Singleton();
}
return instance ;
}
 
    这样,再多的线程访问都只会实例化一个单例对象,实现了多线程的安全访问,但是在多线程高并发访问的情况下,给此方法加上 ynchronized 关键字会是得性能大不如前。
 
 
如何创建并发访问效率高的单例Double-Check Locking
 
    仔细分析发现,使用 synchronized 关键字对整个 getInstance( ) 方法进行同步是没有必要的:我们只要保证实例化这个对象的那段逻辑被一个线程执行就可以了,而返回引用的那段代码是没有必要同步的。更改去下:
public   static  Singleton getInstance() {
if ( instance == null ) {
synchronized (Singleton. class ) {
if ( instance == null ) {
instance = new Singleton();
}
}
}
return instance ;
}
 
    在 getInstance( )方法里,首先判断实例是否已经被创建了,如果还没有创建,首先使用 synchronized 同步实例代码块。在同步代码块里,还需要再次检查是否已经创建了此类的实例,这是因为:如果没有第二次检查,这时有两个线程 Thread A 和 Thread B 同时进入该方法,它们都检测到 instatnce 为 null,不管哪一个线程先占据同步锁,并创建实例对象,都不会阻止另外一个线程继续进入实例代码块重新创建实例对象,这样,同样会产生两个实例对象。所以,我们在同步的代码块里,要进行第二次判断,判断该代码是否已被创建。
 
 
注意:此程序只有在 JAVA 5及以上版本才能正常运行,在以前版本不能保证其正常运行。这是由于 Java平台的内存模式容许 out-of-order writes 引起的,假定有两个线程,Thread 1 和 Thread 2,它们执行以下步骤:
    1、Thread 1发现 instatnce 没有被实例化,它获得锁,并去实例化此对象,JVM 容许在没有完全实例化完成时,instance 变量就指向此实例,因为这些步骤可以是 out-of-order writes 的,此时 instance==null 为 false,之前的版本即使用 volatile 关键字修饰也无效。
    2、在初始化完成之前,Thread 2 进入此方法,发现 instance 已经不为 null了,Thread 2 便认为该实例初始化完成了,使用这个未完成初始化的实例对象,则很可能引起系统的奔溃。
 
 
 
Initialization on demand holder
    要使用线程安全的延迟的单例初始化,还有一种方法,代码如下:
public   class   LazyLoadedSingleton  {
private LazyLoadedSingleton { }
private static class LazyHolder{
private static final LazyLoadedSingleton singletonInstatnce = new LazyLoadedSingleton();
}
public static LazyLoadedSingleton getInstance() {
return LazyHolder.singletonInstatnce;
}
}
 
    当 JVM 加载 LazyLoadedSingleton   类时,由于该类没有 static 属性,所以加载完成后便即可返回。只有第一次调用 getInstance( ) 方法时,JVM 才会加载 LazyHolder 类,由于它包含一个 static 属性 singletonInstatnce,所以会首先初始化这个变量,这样即实现了一个即保证线程安全又支持延迟加载的单例模式。
 
单例模式序列化应该注意的问题Singleton 的序列化
 
    如果单例类实现了 Serializable接口,在默认情况下,每次反序列化总会创建一个新的实例对象,这样一个系统会出现多个对象使用。
    解决思路: readResolve( )方法在反序列化完成之前被执行,我们在此方法里替换掉反序列化出来的那个新的实例,让其指向内存中的那个单例对象即可,代码如下:
public   class  SerializableSingleton  implements  Serializable {
private static final long serialVersionUID = 4285441628073602932L;
static SerializableSingleton singleton = new SerializableSingleton();
private SerializableSingleton() {
}
private Object readResolve () {
return singleton ;
}
}
    方法 readResovle( ) 直接返回 singleton单例,这样,在内存中始终保持了一个唯一的单例对象。
 
 
 
思考:以上学习,讨论的是在同一个 JVM中,保证一个类只有一个单例,如果在分布式环境中,如何保证在整个应用(可能分布在不同 JVM上)只有一个实例???
 

创建对象——单例(Singleton)模式的更多相关文章

  1. 漫谈设计模式(二):单例(Singleton)模式

    1.前言 实际业务中,大多业务类只需要一个对象就能完成所有工作,另外再创建其他对象就显得浪费内存空间了,例如web开发中的servlet,这时便要用到单例模式,就如其名一样,此模式使某个类只能生成唯一 ...

  2. JAVA中实现单例(Singleton)模式的八种方式

    单例模式 单例模式,是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例的特殊类.通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例.即一个类只有一个对象实例. 基本的实现思路 单 ...

  3. 【Java学习笔记之三十】详解Java单例(Singleton)模式

    概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...

  4. 设计一个线程安全的单例(Singleton)模式

    在设计单例模式的时候.尽管非常easy设计出符合单例模式原则的类类型,可是考虑到垃圾回收机制以及线程安全性.须要我们思考的很多其它.有些设计尽管能够勉强满足项目要求,可是在进行多线程设计的时候.不考虑 ...

  5. Android与设计模式——单例(Singleton)模式

    概念: java中单例模式是一种常见的设计模式.单例模式分三种:懒汉式单例.饿汉式单例.登记式单例三种. 单例模式有一下特点: 1.单例类仅仅能有一个实例. 2.单例类必须自己自己创建自己的唯一实例. ...

  6. 单例Singleton模式的两种实现方法

    在设计模式中,有一种叫Singleton模式的,用它可以实现一次只运行一个实例.就是说在程序运行期间,某个类只能有一个实例在运行.这种模式用途比较广泛,会经常用到,下面是Singleton模式的两种实 ...

  7. 设计模式C++描述----01.单例(Singleton)模式

    一.概念 单例模式:其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享. class CSingleton { //公有的静态方法,来获取该实例 public: s ...

  8. OpenJDK源码研究笔记(十三):Javac编译过程中的上下文容器(Context)、单例(Singleton)和延迟创建(LazyCreation)3种模式

    在阅读Javac源码的过程中,发现一个上下文对象Context. 这个对象用来确保一次编译过程中的用到的类都只有一个实例,即实现我们经常提到的"单例模式". 今天,特意对这个上下文 ...

  9. 单例/单体模式(Singleton)

    单例/单体模式(Singleton) 首先,单例模式是对象的创建模式之一,此外还包括工厂模式. 单例模式的三个特点: 1,该类只有一个实例 2,该类自行创建该实例(在该类内部创建自身的实例对象) 3, ...

  10. 《连载 | 物联网框架ServerSuperIO教程》- 8.单例通讯模式开发及注意事项

    1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...

随机推荐

  1. html5视频播放器 一 (改写默认样式)

    一个项目用到了html5视频播放器,于是就写了一个,走了很多坑,例如在chrome中加载视频出现加载异常等 先看看效果 是不是感觉换不错,以下是我播放器改写样式的布局. <!DOCTYPE ht ...

  2. mybatis返回list很智能很简答的,只需要配置resultmap进行类型转换,你dao方法直接写返回值list<对应的object>就行了啊

    mybatis返回list很智能很简答的,只需要配置resultmap进行类型转换,你dao方法直接写返回值list<对应的object>就行了啊 dao方法 public List< ...

  3. java List.add操作可以指定位置

    java List.add操作可以指定位置,addAll也可以指定: 使用: public class Test02 { public static void main(String[] args) ...

  4. 戴尔PowerEdge服务器RAID控制卡的配置

    示例演示环境:PowerEdge R620 + H710p Raid控制卡  + 9 x 300G 10k SAS 硬盘 H310.H710.H810的配置方法与H710P大致相同,在此不再累述. 特 ...

  5. 《编程导论(Java)&#183;3.1.2 方法》之 副作用

    4. 副作用 在一些语言如Pascal中,子程序被分成两种:函数和过程.尽管Java没有强制性地要求将方法区分为命令和函数.然而这样的差别对于良好地设计程序有非常大的帮助[1]. 首先说明一个概念:副 ...

  6. Office 连供打印机无法进纸怎么办 卡纸,塞纸怎么办

    我昨天打印还好好的,今天无法进纸了,哪怕只放一张纸,也是左边进去一点点,然后就塞住了,吸不下去了.   因为你的打印机里面有异物.你把连供拆掉(当心墨水流出来,把墨盒拆掉之后放高一点并用纸巾包住,不要 ...

  7. VC,VB程序button、图标样式美化

    此处的"美化"指的不是通过代码进行美化你的程序.关于想进一步优化自己的程序界面的,最好还是去了解下SkinSharp吧.本文提及的是利用第三方资源编辑软件在不更改程序不论什么框架和 ...

  8. react-native redux 操作

    1.项目目录 2.redux (1)app/redux/action/action.js /** * 步骤一 * 行为 action */ // 定义行为名称 export const CHANGE_ ...

  9. 解决ubuntu中firefox浏览器总是提示找不到server的问题

    这个情况在我机器上常常出现,并且时不时的给你出点问题.可是有些时候等一下就好了.或者把引擎换到百度的话它就又行得通了.. 被这个问题搞得非常烦.上网查了下说是防火墙啊之类的出问题.可是自己弄了后这个问 ...

  10. win下IE设置

    当win7系统时需要升级IE为11版本,需要先安装sp1版本补丁,再装IE11,若还是装不了,可借助第三方平台(电脑管家等)升级安装.或 更新系统再安装IE11 https://jingyan.bai ...