Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign up懒汉式和饿汉式单例模式并没有很大区别 #671
Comments
|
我理解的是饿汉式和懒汉式区别点在于懒汉式在我们的单例对象没有被使用的时候不会将其加载进内,仅此而已,个人觉得两者还是很大区别的。如果说没有区别的话,可能会让很多人有误解。 |
|
我提出这个issue的本意是希望各位同学不要盲目随大流,忽略单例模式类加载和初始化的时机。 |
|
@guang19 DCL这玩意就是个面试题,考察知识点而已(很多--)。正常情况下最好使用饿汉模式。 这两个最大的区别其实就是资源的预分配,static 字段应该是在 准备 阶段就把 链接 地址给变量了(不太确定,想要明确答案的话去 jvm 规范里找),所以饿汉模式只要加载过类,他就会初始化static变量(如果后面根本不会使用他也会分配内存),而懒汉模式需要在调用(也就是有需求使用时)才分配内存,而且也有一次并发问题。 注:类加载的方式有很多种,不一定时真正使用时才加载,比如说: |
|
@jinyahuan 请先弄明白加载和初始化的区别,准备阶段是为static字段初始化零值(0,0L,null),初始化阶段才是赋值(执行new Eager()),而第一次调用的时候才会初始化。在我看来,你的说法与网上大部分传闻的说法没啥区别。。 |
|
自己看jvm规范, |
|
验证方法上面都说了: |
|
@jinyahuan 你说的解析是将常量池中的符号引用解析为直接引用。 你只看到了lazy 和 eager是常量,有符号引用,但并没有看到它们的值。
s 是字面量没错, lazy 和 eager是常量也没错,但是new Eager() 并不能在类加载阶段就确定 eager 的内存。 你所说的 Class.forname() 不过是初始化类8中情况的一种情况而已,Class.forname() 会加载并初始化类。我已经说的很清楚了,
只在初始化类时会执行,那么也就会执行new Eager()。 但我已经说过了,普通情况下,jvm第一次执行getstatic指令才会初始化类,有什么毛病吗?因此我说它们两基本无二又有什么毛病? |
|
我的意思说的很清楚:懒汉式和饿汉式普通使用(不使用其它加载或获取单例的手段)没区别,并且是在1楼就说清楚了。 言尽于此吧,我想我已经说的够清楚了,无需再争论下去了。 |
|
学习了,感谢 |
|
@guang19 很感谢纠正了我错误的知识点 static final 修饰的引用类型确实是在初始化阶段才执行的。至于懒汉饿汉我也不想多说,懂了就是懂了,不懂的以后可能就懂了。 |
|
学习了。 |
|
@guang19 驳斥一下你的观点。 懒汉模式并没有考虑到多线程下A初始化单例对象后对B的可视化问题,也不能解决new Eager()时先得到索引,后分配空间的问题 |
|
类初始化之后还有对象实例化啊,饿汉懒汉应该是指的对象实例化的时机。 |
|
@guankang1314 还是觉得题目的意思是两种设计模式实现之间的区别。 |





先把2中单例模式的代码贴上:
饿汉式:

懒汉式:

懒汉式的单例初始化时间在第一次调用的时候。
然后关于饿汉式的初始化时间,网上大部分传闻说: “饿汉式单例在类加载阶段就已经初始化了,典型的空间换时间...”。
首先痛斥下以讹传讹的前辈们,这并不是开发者的精神。
类的生命周期我给作者提过issue,再复习一下:加载,验证,准备,解析,初始化,使用,卸载。
我想问问前辈们,饿汉式的单例在以上哪个阶段被初始化了?哪里占内存空间了?
以我个人的理解:最多在准备阶段,会给类的静态字段的变量赋零值,引用类型为null。
然后就是那8个初始化时机会初始化饿汉式的单例。而8个时机里面,正常使用,只有第一次获取单例的时候才会初始化单例。
所以我说 饿汉式与懒汉式基本无二。