`
rayloo
  • 浏览: 84392 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

单例模式的几种写法(包含双检锁写法)

阅读更多

饿汉式单例类

Java代码
  1. publicclassSingleton
  2. {
  3. privateSingleton(){
  4. }
  5. privatestaticSingletoninstance=newSingleton();
  6. privatestaticSingletongetInstance(){
  7. returninstance;
  8. }
  9. }

饿汉式提前实例化,没有懒汉式中多线程问题,但不管我们是不是调用getInstance()都会存在一个实例在内存中

内部类式单例类

Java代码
  1. publicclassSingleton
  2. {
  3. privateSingleton(){
  4. }
  5. privateclassSingletonHoledr(){
  6. privatestaticSingletoninstance=newSingleton();
  7. }
  8. privatestaticSingletongetInstance(){
  9. returnSingletonHoledr.instance;
  10. }
  11. }

内部类式中,实现了延迟加载,只有我们调用了getInstance(),才会创建唯一的实例到内存中.并且也解决了懒汉式中多线程的问题.解决的方式是利用了Classloader的特性.

懒汉式单例类

Java代码
  1. publicclassSingleton
  2. {
  3. privateSingleton(){
  4. }
  5. privatestaticSingletoninstance;
  6. publicstaticSingletongetInstance(){
  7. if(instance==null){
  8. returninstance=newSingleton();
  9. }else{
  10. returninstance;
  11. }
  12. }
  13. }

在懒汉式中,有线程A和B,当线程A运行到第8行时,跳到线程B,当B也运行到8行时,两个线程的instance都为空,这样就会生成两个实例。解决的办法是同步:

可以同步但是效率不高:

Java代码
  1. publicclassSingleton
  2. {
  3. privateSingleton(){
  4. }
  5. privatestaticSingletoninstance;
  6. publicstaticsynchronizedSingletongetInstance(){
  7. if(instance==null){
  8. returninstance=newSingleton();
  9. }else{
  10. returninstance;
  11. }
  12. }
  13. }

这样写程序不会出错,因为整个getInstance是一个整体的"critical section",但就是效率很不好,因为我们的目的其实只是在第一个初始化instance的时候需要locking(加锁),而后面取用instance的时候,根本不需要线程同步。
于是聪明的人们想出了下面的做法:

双检锁写法:

Java代码
  1. publicclassSingleton{
  2. privatestaticSingletonsingle;//声明静态的单例对象的变量
  3. privateSingleton(){}//私有构造方法
  4. publicstaticSingletongetSingle(){//外部通过此方法可以获取对象
  5. if(single==null){
  6. synchronized(Singleton.class){//保证了同一时间只能只能有一个对象访问此同步块
  7. if(single==null){
  8. single=newSingleton();
  9. }
  10. }
  11. }
  12. returnsingle;//返回创建好的对象
  13. }
  14. }

思路很简单,就是我们只需要同步(synchronize)初始化instance的那部分代码从而使代码既正确又很有效率。
这就是所谓的“双检锁”机制(顾名思义)。
很可惜,这样的写法在很多平台和优化编译器上是错误的。

原因在于:instance = newSingleton()这行代码在不同编译器上的行为是无法预知的。一个优化编译器可以合法地如下实现instance = new Singleton():

1. instance = 给新的实体分配内存

2. 调用Singleton的构造函数来初始化instance的成员变量

现在想象一下有线程A和B在调用getInstance,线程A先进入,在执行到步骤1的时候被踢出了cpu。然后线程B进入,B看到的是instance 已经不是null了(内存已经分配),
于是它开始放心地使用instance,但这个是错误的,因为在这一时刻,instance的成员变量还都是缺省值,A还没有来得及执行步骤2来完成instance的初始化。

当然编译器也可以这样实现:

1. temp = 分配内存

2. 调用temp的构造函数

3. instance = temp

如果编译器的行为是这样的话我们似乎就没有问题了,但事实却不是那么简单,因为我们无法知道某个编译器具体是怎么做的,因为在Java的memory model里对这个问题没有定义。

双检锁对于基础类型(比如int)适用。很显然吧,因为基础类型没有调用构造函数这一步。

分享到:
评论

相关推荐

    java-单例模式几种写法

    自己总结的6中单例模式的写法,也有测试类,可以试验下,自己稍微修改一下后,验证安全性,纯粹为学习,建议可提

    unity中涉及的三种单例模式

    unity中在场景切换时需要经常数据分享并处理,在此分享给大家利用C#模式和Unity模式分别实现的单例共享数据

    简单了解python单例模式的几种写法

    主要介绍了简单了解python单例模式的几种写法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    线程安全的单例模式的几种实现方法分享

    线程安全的单例模式实现有几种思路,个人认为第2种方案最优雅:、饿汉式、借助内部类、普通加锁解决、双重检测,但要注意写法,如果单体模式继续扩展为N元单体模式,那就是对象池模式了

    面试官的一道简单的单例模式问题给我问懵了,详解单例模式双重检查加锁为什么要加volatile关键字!

    面试官:说说单例模式几种写法? 我:懒汉式和饿汉式,懒汉式巴拉巴拉,饿汉式巴拉巴拉。 面试官:我们都知道synchronized加锁是比较耗费资源的,你这种写法每次访问都需要获得锁(基础的懒汉式写法),效率比较低,...

    详解java中的6种单例写法及优缺点

    在java中,单例有很多种写法,面试时,手写代码环节,除了写算法题,有时候也会让手写单例模式,这里记录一下单例的几种写法和优缺点。需要的朋友可以参考下

    谈一谈iOS单例模式

    单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。...2、具体单例模式的几种模式 第一种单例模式 //非线程安全写法 static UserHelper * helper = nil; + (UserH

    详解Java中如何正确书写单例模式

    一般单例都是五种写法:懒汉,饿汉,双重校验锁,静态内部类和枚举。本文整理了几种常见的单例写法,下面跟着小编一起来看下吧

    DegignPattern.rar

    策略模式实例,单例模式(懒汉最优实例【饿汉模式的几种写法】,经典饿汉模式)后面会持续追加其他设计模式

    C#设计模式.rar

    C# 几种 设计模式Demo 以及优缺点注释 主要有 1单例模式 2简单工厂 3抽象工厂 4委托的另外一种写法 5...

    2023年最新java面试大全

    【06期】单例模式有几种写法? 【07期】Redis中是如何实现分布式锁的? 【08期】说说Object类下面有几种方法呢? 【09期】说说hashCode() 和 equals() 之间的关系? 【10期】Redis 面试常见问答 【11期】分布式...

    精通QTP——自动化测试技术领航

    1.7.2 掌握描述性编程的两种写法 173 1.7.3 Object Identification与Spy结合DP的妙用 177 1.7.4 描述性编程的妙用以及与对象库编程的混搭 180 1.7.5 终极对决—对象库编程(OP)?VS描述性编程(DP) 186 1.7.6 总结 ...

Global site tag (gtag.js) - Google Analytics