关于单例模式的N种实现方式
在开发中经常用到单例模式,单例模式也算是设计模式中最容易理解,也是最容易手写代码的模式,所以也常作为面试题来考。所以想总结一下单例模式的理论知识,方便同学们面试使用。
单例模式实现的方式只有两种类型,一种是饿汉式(类加载时就初始化)、一种是懒汉式(类加载时不初始化)。饿汉式没什么可讲究的因为它既简单也线程安全,如果条件允许一般我们都会直接用饿汉式;唯独比较麻烦的是懒汉式,考虑到线程安全,使用懒汉式的单例模式,就有多种实现方式了。
一、饿汉式
如上所述,饿汉式很简单,实现方式如下:
public class Singleton{
private static final Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
这种单例模式,很简单而且还安全,可以说近乎完美。它唯一的缺陷就是,这种实现方式就无法适用于单例还需要一定的配置或者传参的场景。
二、懒汉式
懒汉式的单例模式,由于要考虑到线程安全的情况,有多种实现方式。
0、如下的实现方式是一种不安全的实现方式
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
之所以把定义为编号0,就是除非你非常确定没有多线程的场景,因为当有多个线程并行调用 getInstance() 的时候,就会创建多个实例。所以绝大部分场景下,这种实现方式都是不应该使用。
1、低性能的懒汉式
public static synchronized Singleton getInstance() {
private static Singleton instance;
private Singleton (){}
if (instance == null) {
instance = new Singleton();
}
return instance;
}
这种实现方式,解决了多线程实例问题,但是由于直接在方法上使用synchronized关键字,即类锁同步,使得只能使用每次调用都要进行同步操作,性能比较低。
2、双重检验锁的懒汉式
public class Singleton {
private volatile static Singleton instance;
private Singleton() {
}
public static Singleton getSingleton() {
if (instance == null) { // null 检测
synchronized (Singleton.class) { // 同步检测
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
我们看到最多的情况就是没有加volatile的情形,其实你不加这个关键字,面试你的人应该也不会纠结于这个,因为99.99%的情况都是好的(要知道那些提供后台服务的,一般只会保证99%可靠性……)。之所以加是因为JVM存在指令重排的优化,导致
new Singleton() 不是一个原子操作行为,new Singleton() 这句话执行的过程大概分为以下abc步骤:
a, 给 instance 分配内存;
b, 调用 Singleton 的构造函数来初始化成员变量;
c, 将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了);
JVM 的即时编译器中存在指令重排序的优化,执行顺序可能是 abc 也可能是 acb。如果是acb,在执行到c,被线程X抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程X会直接返回 instance,使用一个没有初始化的实例产生错误是必然的。那为什么要加volatile呢?一般我们只用到volatile的可见性功能,即对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入,而且经常用于解决同步问题。其实volatile还有一个重要的功能——禁止指令重排优化,也就是volatile标记的变量不会被编译器优化。如上所示,加上volatile以后,读操作一定会发生在abc或者acb之后,并且这个顺序是固定。
3、静态内部类实现懒汉式
public class Singleton {
private static class InternalSingleton {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return InternalSingleton.INSTANCE;
}
}
由于InternalSingleton 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷。
4、通过枚举实现懒汉式
通过枚举写单例超级简单,什么都不用想,这也是它最大的优点。下面这段代码就是声明枚举实例的通常做法。
public enum Singleton {
INSTANCE;
public void show(int age) {
System.out.println("this is show function -> " + age);
}
}
我们可以通过Singleton.INSTANCE来访问实例,这比调用getInstance()方法简单多了。创建枚举默认就是线程安全的,所以不需要担心double checked locking,而且还能防止反序列化导致重新创建新的对象。可能由于平时我们使用枚举都是为了表示类别,大家都很少使用这种方式去写单例模式。
关于单例模式的N种实现方式的更多相关文章
- JAVA中单例模式的几种实现方式
1 线程不安全的实现方法 首先介绍java中最基本的单例模式实现方式,我们可以在一些初级的java书中看到.这种实现方法不是线程安全的,所以在项目实践中如果涉及到线程安全就不会使用这种方式.但是如果不 ...
- Python 单例模式的几种实现方式
单例模式的几种实现方式 先来看几个魔法方法的简单运用:__new__, __init__, __call__. class A(object): def __init__(self, x): prin ...
- Python中的单例模式的几种实现方式的优缺点及优化
单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...
- Python中的单例模式的几种实现方式的及优化
单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...
- CSIC_716_20191129【 单例模式 的五种实现方式】
单例模式 单例模式:在确定类中的属性和方法不变时,需要反复调用该类的情况. 让所有通过该类实例化出的对象,都指向同一个内存地址. 优点:节省内存空间. 单例模式有五种表现形式: 1.通过class ...
- 单例模式的两种实现方式对比:DCL (double check idiom)双重检查 和 lazy initialization holder class(静态内部类)
首先这两种方式都是延迟初始化机制,就是当要用到的时候再去初始化. 但是Effective Java书中说过:除非绝对必要,否则就不要这么做. 1. DCL (double checked lockin ...
- C#单例模式的几种实现方式
一.多线程不安全方式实现 public sealed class SingleInstance { private static SingleInstance instance; private S ...
- python 单例模式的四种创建方式
单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...
- Item 3 ------单例模式的几种实现方式,及优缺点
单例模式,是指一个类只有一个唯一的实例,一个类只会被实例化一次.实现这种效果,最佳的方式,编写包含单个元素的枚举类型. 单例模式的最佳实现方式-----创建一个包含单个元素的枚举类 public en ...
随机推荐
- ado中dispose和close的区别,摘自网络
Close() and Dispose() are basically the same thing on an ADO.NET connection object for providers shi ...
- vue 的过滤器
1.0版本: limitBy filteBy orderBy lowerBy upperBy json currency capitalize pluralize debounce 2.0版本: 要自 ...
- window.screen.height和window.screen.availHeight和document.body.clientHeight和document.documentElement.clientHeight
说这几个属性前 我说一下我的设备 我的设备有两个,一个高度为1080的显示器,一个高度为800的电脑 第一种:window.screen.height 这个方法是获取用户电脑屏幕的高度,是不关浏览器或 ...
- C语言 反序打印字符串中的单词
int main() { char *str = "see you later"; int r = strlen(str); char * p = str; ; while(*st ...
- WordPress主循环(The Loop)函数have_posts(),the_post()详解
WordPress中调用文章标题是the_title();调用文章内容时用到the_content();调用文章的作者时用到the_author();等等这些函数,都需要在主循环中使用,下面就介绍一下 ...
- Nuget私服使用
首先前提是师父已经搭好私服环境了(怎么搭建参考https://www.cnblogs.com/liupengblog/archive/2012/09/10/2678508.html). 然后在vs中打 ...
- 解决报错:Access denied for user 'root'@'xxx' ,MySQL不允许从远程访问的方法 .
原文地址:传送门 例如,你想myuser使用mypassword从任何主机连接到mysql服务器的话. GRANT ALL PRIVILEGES ON *.* TO 'myuser'@'%' IDEN ...
- 如何实现electron多页面间通信
如何实现electron多页面间通信 1,业务需求: 总共有两个页面,页面A显示数据,页面B处理数据,主线程Main 2,实现的技术方案: 在主线程中打开页面A和B,B页面不进行显示,主要负责处理从A ...
- 四十五 常用内建模块 hashlib
Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等. 什么是摘要算法呢?摘要算法又称哈希算法.散列算法.它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制 ...
- Vue组件之props,$emit与$on以及slot分发
组件实例之间的作用域是孤立存在,要让它们之间的信息互通,就必须采用组件的通信方式 props用于父组件向子组件传达信息 1.静态方式 eg: <body> <div id=&quo ...