你真的理解了java单例模式吗?讲别人都忽略的细节!
前言:老刘这篇文章敢做保证,java的单例模式讲的比大多数的技术博客都要好,讲述别人技术博客都没有的细节!!!
1 java单例模式
直接讲实现单例模式的两种方法:懒汉式和饿汉式,单例模式的概念自己上网搜吧这里就不讲了!
这里会涉及到java中的jvm,如果你没有这方面的知识,我建议你先去补补,不然会有点迷糊!
首先说说类什么时候进行加载?
java虚拟机没有进行强制性的约束,但是对于初始化却严格规定了有且只有4种情况必须先对类进行初始化。
我们要知道的是在类加载的过程中,加载、验证、准备是在初始化之前完成的,所以进行了初始化,加载、验证、准备自然就在之前完成了。
然后这四种情况是分别遇到 new 、 getstatic 、 putstatic 和 invokestatic 这四条指令时,如果对应的类没有初始化,则要对对应的类先进行初始化。
讲完类加载时机,就可以讲懒汉式和饿汉式了。
直接先说说懒汉式为什么是线程不安全的?
先看最开始的代码:
public class Student2 { //1:构造私有
private Student2(){}
//2:定义私有静态成员变量,先不初始化
private static Student2 student = null; //3:定义公开静态方法,获取本身对象
public static Student2 getSingletonInstance(){
//没有对象,再去创建
if (student == null) {
student = new Student2();
}
//有对象就返回已有对象
return student;
}
}
结合之前讲的类加载内容,遇到new或加载静态方法了就会进行类加载了。
线程1它new了一个对象,线程2它紧接着也new一个对象,第二个对象的值把第一个对象的值覆盖了,不管new了多少个对象,都会产生垃圾对象,只有最后一个对象才会保持住,其他对象都会变成不可达对象,被垃圾回收,这个过程就相当于产生了大量无效对象,这就是线程不安全的原因!
那为了让懒汉式变得线程安全,我们要怎么做?
看代码:
public class Student4 { private volatile static Student4 student = null;
private Student4() {} public static Student4 getSingletonInstance() {
if (student == null) {//第一个null判断,是先大范围过滤一遍
synchronized (Student4.class) {
if (student == null) {
student = new Student4();
}
}
}
return student;
}
}
这个叫双重检查锁DCL,第一个if先大范围判断是不是空值,经过synchronized,线程1先进去执行完后,线程2才能进去,然后第二个if判断是否完成创建类的实例,线程1创建完了,线程2就不用创建了。
那为什么要加volatile关键字呢?
因为我们Student student = new Student()的执行过程是:
1、new触发类加载机制(已经被加载过的类不需要再次加载)
2、分配内存空间
3、将对象进行初始化4、讲对象引用地址赋值给栈空间中的变量但我们JVM中的JIT即时编辑器会对代码的执行过程进行优化,把过程变为1、2、4、3。
这是什么意思呢?就是未经初始化直接赋值,这样就是student直接有值了,但整个对象还未初始化完成,所以这个对象是不完整的,是个未成品。在JVM规范中,它是一个根本不能用的对象。
到了这个时候,线程1做了这么多事,我们让它休息会,给CPU稍微停一下,线程2就来了,它就直接得到了对象,但它调用对象的方法时,就会报错。虽然这个对象有值,但还未初始化完成。所以我们要加上volatile关键字禁止指令重新排序。
面试重灾区说的差不多了,饿汉式还是要讲讲。
最后就说说饿汉式为什么没有线程安全问题?
看代码:
public class Student1 {
// 2:成员变量初始化本身对象
private static Student1 student = new Student1();
// 构造私有
private Student1() {
}
// 3:对外提供公共方法获取对象
public static Student1 getSingletonInstance() {
return student;
}
public void sayHello(String name) {
System.out.println("hello," + name);
}
}
根据类加载的东西,在多线程的条件下,线程1先执行getSingletonInstance()时,就会进行类加载,类的静态资源就会进行初始化。根据JVM安全机制里说的,当一个类被JVM加载的时候,该类的加载是线程安全的,相当于JVM对该过程加锁了。所以整个过程处于一个锁的范围内,然后静态成员变量进行初始化就相当于Student1()被new了,只会被new一次。
当第二个线程进来,它就发现这个类已经被加载了,就不需要进行加载了,对象也不需要频繁创建,所以线程是安全的!
2 总结
老刘看过很多关于java单例模式的资料,多多少少都会缺少一点细节,这次老刘把它补全了。
最后,如果觉得有哪里写的不好或者有错误的地方,可以联系公众号:努力的老刘,进行交流。
如果觉得写的不错,给老刘点个赞!
你真的理解了java单例模式吗?讲别人都忽略的细节!的更多相关文章
- Java 语法 try catch使用容易忽略的细节 BigDecimal
try catch使用细节 一. try catch的使用方式容易理解,两者最终都要执行finally中的代码,而当return在try和catch中又会有什么效果? 如果我们做一个简单的例子就会发现 ...
- Java内存模型原理,你真的理解吗?
[51CTO.com原创稿件]这篇文章主要介绍模型产生的问题背景,解决的问题,处理思路,相关实现规则,环环相扣,希望读者看完这篇文章后能对 Java 内存模型体系产生一个相对清晰的理解,知其然知其所以 ...
- 你真的理解Java 注解吗?
你真的理解Java 注解吗? 1.什么是注解? 官方解释: Java 注解用于为 Java 代码提供元数据.作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的.Java ...
- Java随谈(六)## 我们真的理解 Java 里的整型吗?
我们真的理解 Java 里的整型吗 整型是我们日常生活中最常用到的基础数据类型,看这篇文章之前,我想问: 我们真的像自己认为的那么理解 Java 内的整型吗? 也许看完本篇文章你就有自己的答案. C ...
- Java内存管理-你真的理解Java中的数据类型吗(十)
勿在流沙筑高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 作为Java程序员,Java 的数据类型这个是一定要知道的! 但是不管是那种数据类型最 ...
- java核心技术-(总结自杨晓峰-java核心技术36讲)
1. 谈谈你对java平台的理解 首先是java最显著的两个特性,一次写入处处运行:还有垃圾收集器gc,gc能够对java内存进行管理回收,程序员不需要关心内存的分配和回收问题 然后谈谈jre和jdk ...
- 细思极恐-你真的会写java吗?
导语 自2013年毕业后,今年已经是我工作的第4个年头了,总在做java相关的工作,终于有时间坐下来,写一篇关于java写法的一篇文章,来探讨一下如果你真的是一个java程序员,那你真的会写java吗 ...
- 细思极恐-你真的会写java吗?
文章核心 其实,本不想把标题写的那么恐怖,只是发现很多人干了几年java以后,都自认为是一个不错的java程序员了,可以拿着上万的工资都处宣扬自己了,写这篇文章的目的并不是嘲讽和我一样做java的同行 ...
- 【设计模式】Java设计模式精讲之原型模式
简单记录 - 慕课网 Java设计模式精讲 Debug方式+内存分析 & 设计模式之禅-秦小波 文章目录 1.原型模式的定义 原型-定义 原型-类型 2.原型模式的实现 原型模式的通用类图 原 ...
随机推荐
- redis new
redis cluster 数据结构 geo,heperloglog 3个非核心dict:阻塞dict,非阻塞dict,watch dict 3个bio线程,生产者消费者模式,主线程生产者: 1.la ...
- @Transactional自调用问题
- golang 自学系列(四)——debug for vscode
golang 自学系列(四)--(调试)VSCode For Debug 这里如何装 vscode 我就不说了 这里如何在 vscode 正常写代码我也不说了 在能正常用 vscode 写 go 语言 ...
- Beta冲刺随笔——Day_Nine
这个作业属于哪个课程 软件工程 (福州大学至诚学院 - 计算机工程系) 这个作业要求在哪里 Beta 冲刺 这个作业的目标 团队进行Beta冲刺 作业正文 正文 其他参考文献 无 今日事今日毕 林涛: ...
- 冲刺随笔——Day_Ten
这个作业属于哪个课程 软件工程 (福州大学至诚学院 - 计算机工程系) 这个作业要求在哪里 团队作业第五次--Alpha冲刺 这个作业的目标 团队进行Alpha冲刺 作业正文 正文 其他参考文献 无 ...
- Java数据结构(十三)—— 二叉排序树(BST)
二叉排序树(BST) 需求 给定数列{7,3,10,12,5,1,9},要求能够高效的完成对数据的查询和添加 思路三则 使用数组,缺点:插入和排序速度较慢 链式存储,添加较快,但查找速度慢 使用二叉排 ...
- SQL Server 索引碎片整理
索引碎片整理的四种方法: 1)删除索引并重建 2)使用 DROP_EXISTING 语句重建索引 3)使用 ALTER INDEX REBUILD 语句重建索引 4)使用 ALTER INDEX RE ...
- day3(Vue组件)
1.组件定义 1.定义组件并引用 2.父组件向子组件传值 3.子组件向父组件传值 # 组件间传值:vuex (https://www.cnblogs.com/xiaonq/p/9697921.html ...
- day6(celery原理与组件)
1.Celery介绍 1.1 celery应用举例 Celery 是一个 基于python开发的分布式异步消息任务队列,通过它可以轻松的实现任务的异步处理,如果你的业务场景中需要用到异步任务,就可以考 ...
- MySQL索引(一)索引基础
索引是数据库系统里面最重要的概念之一.一句话简单来说,索引的出现其实是为了提高数据查询的效率,就像书的目录一样. 常见模型 索引的出现是为了提高查询效率,但是实现索引的方式却有很多种,这里就介绍三种常 ...