单利模式

单利模式简单的来说由两部分组成:

  1. 只实例化一次
  2. 提供返回这个实例的方法

单利模式的好坏在于线程安全、性能(效率)、懒加载这三个属性的好坏

那怎么实现单利模式呢?也就是怎么才能保证一直只有一个实例呢?
很快我们就能想到static关键字

饿汉模式

public class HungerySingletonDemo {private static HungerySingletonDemo instance=new HungerySingletonDemo();private HungerySingletonDemo() {}public static HungerySingletonDemo getInstance() {return instance;}
}

线程安全、性能好,但是不能实现懒加载,当HungerySingletonDemo占用变大时会非常占用内存(一开始不使用这个对象却要占用大量内存)

懒汉模式

先要实现懒加载,就在第一次调用的时候实例它好了

public class HoonSingleton {private static HoonSingleton instance=null;private HoonSingleton() {}public static  HoonSingleton getInstance() {if(instance==null) {instance = new HoonSingleton();}return instance;}
}

实现了懒加载,但是线程不安全,因为如果当多个线程同时访问到if(instance==null) 这行代码,都会执行到 if 内部,这样就会创建多个实例。线程都不安全就没有性能好坏这个说法了。
那有没有这么方法来改进呢?也就是怎么控制线程的并发访问呢?
很明显我们会想到synchronized关键字。

public static synchronized HoonSingleton getInstance()

这样的话线程就可同步访问getInstance这个方法,就不会创建这个实例了
但问题又随着而来了,synchronized效率太低了,性能不好。那怎么才能提高性能呢?这时,我们可能会想到让同步代码执行次数少点不就好了吗,那怎么才能使同步代码执行次数变少呢?请看下例DCL。

DCL(Double Check Lock)

public static  HoonSingleton getInstance() {if(instance==null) {synchronized(HoonSingleton.class) {instance = new HoonSingleton();}}return instance;
}

如上,是不是减少了同步代码的执行次数(instance==null就不会执行同步代码)
但是这样的话有出现了线程安全的问题,因为如果有多个线程都进入到instance==null 里面的 if 语句块,就会在synchronized代码块进行并发操作,无论如何最后都会并发完,结果也会创建多个实例。那如何改进呢?这就是DCL(执行两次判断语句)。

public class DCLSingleton {private static DCLSingleton instance=null;private DCLSingleton() {}public static DCLSingleton getInstance() {if(instance==null) {synchronized(DCLSingleton.class) {if(instance==null) {instance = new DCLSingleton();}}}return instance;}
}

如上,判断了两次instance==null ,这就防止了创建多个实例(synchronized内部再判断一次,如果不为空就不创建实例)。但这个有可能会存在问题,如下:
存在问题
假设线程一执行到instance = new DCLSingleton();这句,这里看起来是一句话,但实际上其被编译后在JVM执行的对应会变代码就发现,这句话被编译成8条汇编指令,大致做了三件事情:

1)给instance实例分配内存;

2)初始化instance的构造器;

3)将instance对象指向分配的内存空间(注意到这步时instance就非null了)

如果指令按照顺序执行倒也无妨,但JVM为了优化指令,提高程序运行效率,允许指令重排序。如此,在程序真正运行时以上指令执行顺序可能是这样的:

a)给instance实例分配内存;

b)将instance对象指向分配的内存空间;

c)初始化instance的构造器;

这时候,当线程一执行b)完毕,在执行c)之前,被切换到线程二上,这时候instance判断为非空,此时线程二直接来到return instance语句,拿走instance然后使用,接着就顺理成章地报错(对象尚未初始化)。

具体来说就是synchronized虽然保证了线程的原子性(即synchronized块中的语句要么全部执行,要么一条也不执行),但单条语句编译后形成的指令并不是一个原子操作(即可能该条语句的部分指令未得到执行,就被切换到另一个线程了)。

根据以上分析可知,解决这个问题的方法是:禁止指令重排序优化(相对的),即使用volatile变量。

 private static volatile  DCLSingleton instance=null;//在该属性上加volatile

Holder模式(使用广泛)

声明类时不声明实例变量,而放到一个内部静态类中去实例化该类

public class HolderSingleton {private static class Holder{private static HolderSingleton instance=new HolderSingleton();}public static HolderSingleton getInstance() {return HolderSingleton.Holder.instance;}
}

加载HolderSingleton时,也会加载内部类Holder,但它不会初始化,当首次调用getInstance时才初始化化(就是实现了懒加载)。

枚举模式(使用广泛)

Holder模式的内部类替换成enum类型的,因为枚举类型的成员时(INSTANCE)也是public static final的,且是在static{}中初始化的(就是实现了懒加载)。

public class EnumSingleton {private EnumSingleton() {}private enum EnumHolder{INSTANCE;//public static final EnumHolder实例,在static块初始化private EnumSingleton instance=null;//EnumHolder实例的私有变量EnumHolder(){instance = new EnumSingleton();}}public static EnumSingleton getInstance() {return EnumHolder.INSTANCE.instance;}}

单例模式(DCL、holder等)相关推荐

  1. java单例模式(Holder模式美滋滋)

    饱汉模式(懒汉模式) // 饱汉// 非线程安全public class Singleton1 {private static Singleton1 singleton = null;private ...

  2. java 单例 dcl_java 中单例模式DCL的缺陷及单例的正确写法

    1 前言 单例模式是我们经常使用的一种模式,一般来说很多资料都建议我们写成如下的模式: /** * Created by qiyei2015 on 2017/5/13. */ public class ...

  3. 面试官:你写的单例模式有空指针异常,请你用Volatile改一下。我愣了五分钟...

    1 单例模式 大家对单例模式并不会陌生,当创建一个对象需要消耗比较多资源时,例如创建数据库连接和消息服务端等等,这时我们选择只创建一份这种类型的对象并在进程内共享. 但是单例模式想要写好并不容易,我们 ...

  4. Android设计模式之——单例模式

    一.介绍 单例模式是应用最广的模式之一,也可能是很多初级工程师唯一会使用的设计模式.在应用这个模式时,单例对象的类必须保证只有一个实例存在.许多时候整个系统只需要拥有一个全局对象,这样有利于我们协调系 ...

  5. 高并发下线程安全的单例模式(最全最经典,值得收藏)

    作者:mlinge-奋斗吧 blog.csdn.net/cselmu9/article/details/51366946 在所有的设计模式中,单例模式是我们在项目开发中最为常见的设计模式之一,而单例模 ...

  6. Dubbo流程及源码分析(一)

    扑街前言:之前的文章说明了zookeeper的使用及源码,那么本次我们继续了解和zookeeper的黄金搭档dubbo的相关内容,当然dubbo也是典型的rpc框架,所以我们从客户端和服务端逐个分析, ...

  7. 多线程进阶(并发编程JUC)

    多线程进阶(并发编程JUC) 提示: 本材料只做个人学习参考,不作为系统的学习流程,请注意识别!!! 并发编程JUC 1. 基础知识 什么是JUC(Java并发包)?并发编程的本质(充分利用CPU的资 ...

  8. 线程间怎么交换数据_2 万字长文详解 10 大多线程面试题|原力计划

    作者 | ZZZhonngger 责编 | 伍杏玲 出品 | CSDN博客 Volatile相关 1.请谈谈你对 volatile 的理解 答:volatile 是 Java 虚拟机提供的轻量级的同步 ...

  9. 互联网大厂高频重点面试题

    1.volatile是Java虚拟机提供的轻量级的同步机制 volatile提供的轻量级的同步机制 1.1保证可见性 1.2不保证原子性 1.3禁止指令重排 1.4JMM(Java Memory Mo ...

最新文章

  1. 一致性哈希算法介绍,及java实现
  2. AI公开课:19.04.04李航—字节跳动AILab总监《深度学习与自然语言处理:评析与展望》课堂笔记以及个人感悟
  3. sql两个数字之差取最接近的_从零学DAX/Sql/Python030203SQL数据分类汇总续篇
  4. 深度学习:在图像上找到手势_使用深度学习的人类情绪和手势检测器:第1部分
  5. mac xampp连接mysql数据库_在mac上如何使用终端打开XAMPP自带的MySQL
  6. 客户和顾客是一个意思吗_履约保证金和投标保证金是一个意思吗?
  7. ipv6 6rd前缀计算
  8. uva 10098 Generating Fast(全排列)
  9. 技术大会值得参加吗?
  10. Oracle 字符串批量替换
  11. dlan android手机,电脑与手机远程互通八款DLNA安卓手机推荐
  12. 线性代数 第二章 矩阵 知识点总结(Jeff自我感悟)
  13. 组合数学在软件领域的运用
  14. 计算机毕业设计项目推荐 - 毕设开题选题
  15. evus是什么意思_美国签证和EVUS之间的区别是什么
  16. 试穿APP的NABCD
  17. 119全国消防日,我们要注意用火安全
  18. 基于PHP+MySQL音乐相册网站的设计与实现
  19. ArcGIS用土地利用数据导出shp
  20. 3d渲染服务器系统,3d渲染云服务器

热门文章

  1. windows+Texstudio+languagetool修改语法错误
  2. 程序员常用软件介绍--持续更新
  3. 《王煜全创新生态报告12讲》学习笔记
  4. Pinia——Actions
  5. 关于Odoo盘盈成本和计价法的讨论
  6. VC++6.0 Win32应用程序 如何添加窗体 ------阿冬专栏
  7. deep learning in NLP
  8. 代理是什么?(HTTP代理,SOCKS代理)
  9. 【机器人学习】MPU6050数据的换算
  10. 如何理解CNN中的感受野(receptive-field)以及如何计算感受野?