目前比较常见的有4种(DCL为懒汉模式的线程安全版本)。

单例模式的实现一般需要满足以下条件:

1.构造方法私有化,实例属性私有化。

2.必须仅在类的内部完成实例的初始化过程。

3.提供公共静态方法,用以返回已经初始化完成的实例。

4.不可通过反射,反序列化方式获得新的实例。

1.饿汉模式:进行类初始化时就完成实例初始化的方式。可类比月光族,有钱就买。用不用不知道。

优势:由于初始化较早,所以相对于其他模式节省了这部分时间,效率较高。且多线程环境中保证安全运行。

劣势:在当前单例一直未被使用的场景下,资源利用率变的很低,另外无法从外界传递参数用来实例化。

 /**
* @author txba6868
* @title: SingleTonTest
* @projectName javaAnalysis
* @date 2020/1/8 11:08
* @desc 单例模式
*/
public class SingleTonTest { //饿汉式:以空间换时间,类初始化时完成
private static SingleTonTest singleTonTest = new SingleTonTest(); public static SingleTonTest getInstance() {
return singleTonTest;
}
}

2.懒汉模式:有使用需求时通过调用getInstance()方法实现实例初始化。

优势:按需初始化,有效提高资源利用率。第一次调用可动态传参实例化。

劣势:初始化时出现时间成本,在多线程环境下有较高可能出现安全问题,造成多次实例化。

 /**
* @author txba6868
* @title: SingleTonTest
* @projectName javaAnalysis
* @date 2020/1/8 11:08
* @desc 单例模式
*/
public class SingleTonTest { //懒汉式:以时间换空间,存在多线程安全问题
private static SingleTonTest singleTonTest = null; public static SingleTonTest getInstance() {
if (singleTonTest == null) {
singleTonTest = new SingleTonTest();
}
return singleTonTest;
}
}

3.DCL(doule check locking)主要是为了解决懒汉模式带来的线程安全问题做出的优化。但DCL仍旧存在安全问题,主要是因为程序中的“new”操作并非原子操作,代码注释中已写明,涉及到指令重排序及可见性问题,在java1.5之后JMM提供了volatile关键字,通过内存屏障的方式解决了该问题(网上有言论称volatile并未完美解决,仍然存在安全问题,还待博主继续探索下)。并且DCL由于内存屏障的原因,有可能屏蔽一些效率较高的代码优化,反而会引起效率的下降。

 /**
* @author txba6868
* @title: SingleTonTest
* @projectName javaAnalysis
* @date 2020/1/8 11:08
* @desc 单例模式
*/
public class SingleTonTest { //构造方法私有化(略)
//DCL模式:同样存在线程安全问题,因为"new"操作不是一个原子操作,需要3步执行
//1.在内存中申请空间,2.实例中各参数初始化 3.将实例指向内存地址。
//由于指令重排序,会导致2和3的顺序发生变化,其他等待实例初始化的线程获取到不完整的实例。
private static volatile SingleTonTest singleTonTest = null; public static SingleTonTest getInstance() {
if (singleTonTest == null) {
synchronized (SingleTonTest.class) {
if (singleTonTest == null) {
singleTonTest = new SingleTonTest();
}
}
}
return singleTonTest;
}
}

4.静态内部类:同样是懒加载中的一种初始化方式。类初始化不会引起内部类的初始化。只有调用getInstance()方法时才会执行。该方法是effective java一书中的推荐方式。

优势:代码简洁,线程安全(依据为根据java规范,类的初始化有且仅有一次),按需初始化。

劣势:同饿汉模式,无法通过传递参数实例化对象。

 /**
* @author txba6868
* @title: SingleTonTest
* @projectName javaAnalysis
* @date 2020/1/8 11:08
* @desc 单例模式
*/
public class SingleTonTest { //静态内部类
private static class SingleTonInnerClass {
private static SingleTonTest singleTonTest = new
SingleTonTest();
} public static SingleTonTest getInstance() {
return SingleTonInnerClass.singleTonTest;
}
//构造方法私有化
private SingleTonTest(){} }

另外,以上方式都存在两个同样的问题:可通过reflect创建实例;可通过反序列化生成新的实例。当然,我们可以通过在构造函数中加入一个标识阻止实例的二次创建。反序列化时通过重写方法直接返回当前实例。

但这样做无疑增加了代码的复杂性。

5.枚举类型:枚举方式在jvm层面可防御上述两种攻击方式。目前来看是一种较为安全的单例模式,不过枚举方式在内存方面的占用较静态方法略大。

 /**
* @author txba6868
* @title: SingleTonTest
* @projectName javaAnalysis
* @date 2020/1/8 11:08
* @desc 单例模式
*/
public class SingleTonTest { //构造方法私有化
private SingleTonTest() {
} //获取单例对象
public static SingleTonTest getInstance() {
return SingleTon.INSTANCE.getInstance();
} private enum SingleTon {
INSTANCE;
private SingleTonTest singleTon; SingleTon() {
singleTon = new SingleTonTest();
} private SingleTonTest getInstance() {
return singleTon;
}
}
}

单例模式应用场景:spring IOC容器,日志处理/配置文件读取(后续会补充)。

参考:https://www.jianshu.com/p/f8267d6ac017(细说java中的几种单例模式)

引申:https://www.cnblogs.com/duanxz/p/3152574.html(单例-被废弃的DCL双重检查加锁)

https://www.jianshu.com/p/d9d9dcf23359(为什么说枚举是最好的单例实现)

// 构造方法私有化(略)

java中的几种单例模式的更多相关文章

  1. Java中的五种单例模式实现方法

    [代码] Java中的五种单例模式实现方法   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 2 ...

  2. Java中的五种单例模式

    Java模式之单例模式: 单例模式确保一个类只有一个实例,自行提供这个实例并向整个系统提供这个实例. 特点: 1,一个类只能有一个实例 2 自己创建这个实例 3 整个系统都要使用这个实例 例: 在下面 ...

  3. 快速理解Java中的五种单例模式

    解法一:只适合单线程环境(不好) package test; /** * @author xiaoping * */ public class Singleton { private static S ...

  4. 快速理解Java中的七种单例模式

    饿汉式(推荐) package concurencyv2.chapter1; public class SingletonV2 { private static final SingletonV2 i ...

  5. 快速理解Java中的五种单例模式(转)

    解法一:只适合单线程环境(不好) package test; /** * @author xiaoping * */ public class Singleton { private static S ...

  6. [设计模式](转)Java中的24种设计模式与7大原则

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...

  7. JAVA基础学习之throws和throw的区别、Java中的四种权限、多线程的使用等(2)

    1.throws和throw的区别 throws使用在函数外,是编译时的异常,throw使用在函数内,是运行时的异常 使用方法 public int method(int[] arr) throws ...

  8. JAVA中的四种引用以及ReferenceQueue和WeakHashMap的使用示例

    简介: 本文主要介绍JAVA中的四种引用: StrongReference(强引用).SoftReferenc(软引用).WeakReferenc(弱引用).PhantomReference(虚引用) ...

  9. Java中的四种引用

    引用定义 实际上,Java中存在四种引用,它们由强到弱依次是:强引用.软引用.弱引用.虚引用.下面我们简单介绍下这四种引用: 强引用(Strong Reference):通常我们通过new来创建一个新 ...

随机推荐

  1. ios APP进程杀死之后和APP在后台接收到推送点击跳转到任意界面处理

    https://www.jianshu.com/p/ce0dc53eb627 https://www.cnblogs.com/er-dai-ma-nong/p/5584724.html github: ...

  2. IVM sdk command

    1.load sdk shell ./auto_load_user.sh 2.查看all端口状态 IVM:0>ifcs show devport 3.查看个别端口状态 IVM:0>ifcs ...

  3. BKP寄存器

    STM32的BKP: 备份寄存器是42个16位的寄存器,可用来存储84个字节的用户应用程序数据.他们处在备份域里,当VDD电源被切断,他们仍然由VBAT维持供电.当系统在待机模式下被唤醒,或系统复位或 ...

  4. springmvc面试问题

    1.讲下SpringMvc和Struts1,Struts2的比较的优势 性能上Struts1>SpringMvc>Struts2 开发速度上SpringMvc和Struts2差不多,比St ...

  5. freemarker.core.InvalidReferenceException: [... Exception message was already printed; see it above ...]

    FreeMarker template error:The following has evaluated to null or missing:==> product  [in templat ...

  6. RGB 和 YUV 的转换公式

  7. 2019年的代码都写完了吗?不如做个Python进度条看看还剩多少

    我们都知道,进度条是用来直观展示流程所需时间的优秀工具,以免我们担心流程会突然挂掉,而且我们可以用它来预测代码运行是否正常,借助进度条,每个人都能直观地看到脚本最新的进展情况. 如果你之前没用过进度条 ...

  8. 搭建 nginx + rtmp 媒体服务器笔记

    工作需要搭建一个流媒体服务器,用来接收前端推过来的视频流,达到实时保存的目的. 具体步骤网上已经比较详细了 可以参考下面这个文档参考文档 https://www.cnblogs.com/monjeo/ ...

  9. LibreOJ #2006. 「SCOI2015」小凸玩矩阵

    想了挺久没想出来,一看题解恍然大悟.一个数对应一行和一列,二分答案,凡是小于等于答案的就连边.如果满足能够取出 \(n - k + 1\) 个不比二分中点 \(mid\) 大的数,那么r = mid, ...

  10. 【快学springboot】9.使用 @Transactional 注解配置事务管理

    介绍 springboot对数据库事务的使用非常的方便,只需要在方法上添加@Transactional注解即可.Spring 为事务管理提供了丰富的功能支持.Spring 事务管理分为编程式和声明式的 ...