- 浏览: 205851 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
zhangyanqiu:
能具体的说明一下,例如几个例子么???
实现数据库兼容并不象你想象的那么难 -
lzy.je:
DCL成熟的
对于单例模式的一点想法 -
okhaoba:
我觉得懒汉模式,即第一种足矣。
对于单例模式的一点想法 -
insiku:
想不到javaeye上 不看清楚就乱回帖的人也这么多
对于单例模式的一点想法 -
icewubin:
还在讨论synchronized?第二页buaawhl 说的那 ...
对于单例模式的一点想法
单例模式很普遍,对于Spring的实现机制不清楚,单就Java语言上的实现机制来讨论。
虽然简单,但要获得一个高性能且线程安全的单例确不简单。
最简单的、成熟的单例实现有如下两种:
1.
即在声明静态变量时就实例化。这种方法的问题是,不能传入构造参数从而动态的创建实例。
2.
即在方法上同步。这种方法的问题是,始终有同步的开销(虽然对很多应用来说这开销并不大,以致不需要考虑),而更理想的情况是,读操作不需要同步,只在创建实例时同步。
看上去更好的方法(但有问题!)是:
Double-checked synchronization,
如:
问题解释如下:
参考1:http://www.ibm.com/developerworks/java/library/j-dcl.html
参考2:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
在参考1中提到,out-of-order writes是原因,就是说INSTANCE=new Singleton();这行代码并不是一定按如下伪代码顺序进行的:
1.分配内存
2.调用构造器
3.赋值给INSTANCE
在有的JIT上会编译优化为:
1.分配内存
2.赋值给INSTANCE
3.调用构造器
这就是所谓的out-of-order writes。则问题会出在第2步:此时判断(INSTANCE==null)已经返回真了,但构造器还未调用完成,此时访问INSTANCE则会出现不可预料的问题。
以上都是简单的重复广为人知的知识,下面是我的补充:
在参考2中的"It will work for 32-bit primitive values"一节给了我启发,它提到对32位的原始类型的Double-checked locking是可以的,(我认为实际关键点在于
赋值操作是否是原子的)。既然对int的赋值是原子的,我们可以稍加改进,引入一个int hasInitialized:
区别在于:
以hasInitialized==0来判断是否初始化完成,而在NSTANCE=new Singleton();之后才赋值以确认初始化完成。
这样不是既可保持高性能(绝大部分情况下没有锁,不进入需同步的块)、又可保证线程安全么?
谢谢提醒,我已经修改了,笔误。
我个人觉得还是不要把synchronized放在方法头, 这样子每次调用把个方法都要先锁一下感觉很别扭
不要是试图使用双重检查来 解决这个问题。
在java与模式中作者讲得很明白了。
在多线程的程序中,只能尽量降低同步的几率来保证单例的唯一性。
下面这这样就就是基于上面的理由。
而这样做你是完全依赖于双重检查。
我已经说的很清楚了,你这个就是变相的doublechecking。实际上没有本质区别。
你的问题在于,instance == null这个是不安全的。从前面的参考资料中可以得知,instance = new HashCodeDigest();这句话可能会这样执行:
1.给instance赋值(此时不是null了!!!)
2.调用构造器。(此时才初始化,而在前一步就通过检查了,这就是问题所在)。
不要是试图使用双重检查来 解决这个问题。
在java与模式中作者讲得很明白了。
在多线程的程序中,只能尽量降低同步的几率来保证单例的唯一性。
下面这这样就就是基于上面的理由。
而这样做你是完全依赖于双重检查。
试试这样
这个跟LZ的最后一种方法类似
只是如果JIT连sysRole = _sysRole;也优化的话 那就又有问题了
我最后提出的一种方案不行么?
看看附件的文章。最后的结论是:
“Best practice is that if a variable is ever to be assigned by one thread and used or assigned by another, then all accesses to that variable should be enclosed in synchronized methods or synchronized statements.”
因为这里的很多人很浮躁,以为这么简单的问题自己早就知道了。其实是错的。
JVM 不保证代码INSTANCE=new Singleton() 一定在hasInitialized=1之前执行!!!
这个不会吧?你的证据呢?
我提到的out-of-order write只是对INSTANCE=new Singleton()范围而言。
连两条语句的顺序都无法保证,这个就难以想象了。。。
确实有这个可能的,如果两条语句的顺序改变不会影响执行结果,编译器有可能为某种优化对其进行顺序调整,我在《java concurrecy in practice》中看到的。
你这个就是我在最开始提到的普通的double-checking的做法,static synchronized method等效于synchonized(Singleton.class).
一样会有问题。
static final会直接编译成常量,也就是Test1中的Single.NAME其实是直接初始化成常量,并没有调用到Singleton。看看这个帖子 http://www.iteye.com/topic/211937
是这样的,访问static final不属于主动调用。
虽然简单,但要获得一个高性能且线程安全的单例确不简单。
最简单的、成熟的单例实现有如下两种:
1.
public static final Singleton INSTANCE=new Singleton();
即在声明静态变量时就实例化。这种方法的问题是,不能传入构造参数从而动态的创建实例。
2.
public static synchronized Singleton getInstance(){...}
即在方法上同步。这种方法的问题是,始终有同步的开销(虽然对很多应用来说这开销并不大,以致不需要考虑),而更理想的情况是,读操作不需要同步,只在创建实例时同步。
看上去更好的方法(但有问题!)是:
Double-checked synchronization,
如:
private static Singleton INSTANCE; public static Singleton getInstance(){ if(INSTANCE==null){ synchronized(Singelton.class){ //Double checking if(INSTANCE==null){ INSTANCE=new Singleton(); } } } }
问题解释如下:
参考1:http://www.ibm.com/developerworks/java/library/j-dcl.html
参考2:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
在参考1中提到,out-of-order writes是原因,就是说INSTANCE=new Singleton();这行代码并不是一定按如下伪代码顺序进行的:
1.分配内存
2.调用构造器
3.赋值给INSTANCE
在有的JIT上会编译优化为:
1.分配内存
2.赋值给INSTANCE
3.调用构造器
这就是所谓的out-of-order writes。则问题会出在第2步:此时判断(INSTANCE==null)已经返回真了,但构造器还未调用完成,此时访问INSTANCE则会出现不可预料的问题。
以上都是简单的重复广为人知的知识,下面是我的补充:
在参考2中的"It will work for 32-bit primitive values"一节给了我启发,它提到对32位的原始类型的Double-checked locking是可以的,(我认为实际关键点在于
赋值操作是否是原子的)。既然对int的赋值是原子的,我们可以稍加改进,引入一个int hasInitialized:
private static int hasInitialized=0; private static Singleton INSTANCE; public static Singleton getInstance(){ if(hasInitialized==0){ synchronized(Singelton.class){ //Double checking if(hasInitialized==0){ INSTANCE=new Singleton(); hasInitialized=1; } } } }
区别在于:
以hasInitialized==0来判断是否初始化完成,而在NSTANCE=new Singleton();之后才赋值以确认初始化完成。
这样不是既可保持高性能(绝大部分情况下没有锁,不进入需同步的块)、又可保证线程安全么?
评论
43 楼
lzy.je
2008-08-22
DCL成熟的
42 楼
okhaoba
2008-08-22
我觉得懒汉模式,即第一种足矣。
41 楼
insiku
2008-07-16
想不到javaeye上 不看清楚就乱回帖的人也这么多
40 楼
icewubin
2008-07-15
还在讨论synchronized?第二页buaawhl 说的那个Initialization on Demand Holder (IODH),不是非常好的解决方案么?
比“饿汉式”更充分的lazy,比“双重检查”或“懒汉式(同步)”性能更好,代码也简捷。
比“饿汉式”更充分的lazy,比“双重检查”或“懒汉式(同步)”性能更好,代码也简捷。
39 楼
xin_feng_08
2008-07-15
Lucas Lee 写道
weiqingfei 写道
private static int hasInitialized=0;
private static Singleton INSTANCE;
public static synchronized Singleton getInstance(){
if(hasInitialized==0){
synchronized(Singelton.class){
//Double checking
if(hasInitialized==0){
INSTANCE=new Singleton();
hasInitialized=1;
}
}
}
}
private static Singleton INSTANCE;
public static synchronized Singleton getInstance(){
if(hasInitialized==0){
synchronized(Singelton.class){
//Double checking
if(hasInitialized==0){
INSTANCE=new Singleton();
hasInitialized=1;
}
}
}
}
谢谢提醒,我已经修改了,笔误。
我个人觉得还是不要把synchronized放在方法头, 这样子每次调用把个方法都要先锁一下感觉很别扭
38 楼
LucasLee
2008-07-15
qfs_v 写道
不要是试图使用双重检查来 解决这个问题。
在java与模式中作者讲得很明白了。
在多线程的程序中,只能尽量降低同步的几率来保证单例的唯一性。
下面这这样就就是基于上面的理由。
private static HashCodeDigest instance = null; private HashCodeDigest() { } private static synchronized void syncInit() { if (instance == null) { instance = new HashCodeDigest(); } } public static HashCodeDigest getInstance() { if (instance == null) { syncInit(); } return instance; }
而这样做你是完全依赖于双重检查。
private static Singleton INSTANCE; public static Singleton getInstance(){ if(INSTANCE==null){ synchronized(Singelton.class){ //Double checking if(INSTANCE==null){ INSTANCE=new Singleton(); } } } }
我已经说的很清楚了,你这个就是变相的doublechecking。实际上没有本质区别。
你的问题在于,instance == null这个是不安全的。从前面的参考资料中可以得知,instance = new HashCodeDigest();这句话可能会这样执行:
1.给instance赋值(此时不是null了!!!)
2.调用构造器。(此时才初始化,而在前一步就通过检查了,这就是问题所在)。
37 楼
lcllcl987
2008-07-15
既然在分布式应用中不赞成使用singleton。
那么如何在分布式应用中使用某种方式, 达到类似singleton的效果?
比如我要维持一个单态instance, 此单态instance是有状态的, 因为Application需要随时通过此单态instance读取或者修改某变量(比如修改或读取此单态中的一个map)。
难道只能把状态持久化?
再次强调, 是分布式应用(EJB)。
那么如何在分布式应用中使用某种方式, 达到类似singleton的效果?
比如我要维持一个单态instance, 此单态instance是有状态的, 因为Application需要随时通过此单态instance读取或者修改某变量(比如修改或读取此单态中的一个map)。
难道只能把状态持久化?
再次强调, 是分布式应用(EJB)。
36 楼
qfs_v
2008-07-15
不要是试图使用双重检查来 解决这个问题。
在java与模式中作者讲得很明白了。
在多线程的程序中,只能尽量降低同步的几率来保证单例的唯一性。
下面这这样就就是基于上面的理由。
private static HashCodeDigest instance = null; private HashCodeDigest() { } private static synchronized void syncInit() { if (instance == null) { instance = new HashCodeDigest(); } } public static HashCodeDigest getInstance() { if (instance == null) { syncInit(); } return instance; }
而这样做你是完全依赖于双重检查。
private static Singleton INSTANCE; public static Singleton getInstance(){ if(INSTANCE==null){ synchronized(Singelton.class){ //Double checking if(INSTANCE==null){ INSTANCE=new Singleton(); } } } }
35 楼
insiku
2008-07-15
netrice 写道
public static SysRoleDAO getInstance() { if (sysRole == null) { synchronized (SysRoleDAO.class) { if (sysRole == null) { SysRoleDAO _sysRole = new SysRoleDAO(); sysRole = _sysRole; } } } return sysRole; }
试试这样
这个跟LZ的最后一种方法类似
只是如果JIT连sysRole = _sysRole;也优化的话 那就又有问题了
34 楼
maowc
2008-07-14
用Enum
33 楼
runthu
2008-07-14
单例模式用在java web问题多多。
32 楼
biubiu
2008-07-14
Lucas Lee 写道
biubiu 写道
除了你已经提出的两种办法,没有其他可以run everywhere的办法了。
我最后提出的一种方案不行么?
看看附件的文章。最后的结论是:
“Best practice is that if a variable is ever to be assigned by one thread and used or assigned by another, then all accesses to that variable should be enclosed in synchronized methods or synchronized statements.”
31 楼
biubiu
2008-07-14
spiritfrog 写道
这贴为什么给新手帖这么多?
难道都理解了?
ibm这篇http://www.ibm.com/developerworks/java/library/j-dcl.html
out-of-order writes往后,就很难看下去了。
难道都理解了?
ibm这篇http://www.ibm.com/developerworks/java/library/j-dcl.html
out-of-order writes往后,就很难看下去了。
因为这里的很多人很浮躁,以为这么简单的问题自己早就知道了。其实是错的。
30 楼
microjuz
2008-07-14
Lucas Lee 写道
slangmgh 写道
JVM 不保证代码INSTANCE=new Singleton() 一定在hasInitialized=1之前执行!!!
这个不会吧?你的证据呢?
我提到的out-of-order write只是对INSTANCE=new Singleton()范围而言。
连两条语句的顺序都无法保证,这个就难以想象了。。。
确实有这个可能的,如果两条语句的顺序改变不会影响执行结果,编译器有可能为某种优化对其进行顺序调整,我在《java concurrecy in practice》中看到的。
29 楼
ellen_yang
2008-07-11
路过,学习了
28 楼
LucasLee
2008-07-10
lefish 写道
这样可能会好一点.
private static HashCodeDigest instance = null; private HashCodeDigest() { } private static synchronized void syncInit() { if (instance == null) { instance = new HashCodeDigest(); } } public static HashCodeDigest getInstance() { if (instance == null) { syncInit(); } return instance; }
你这个就是我在最开始提到的普通的double-checking的做法,static synchronized method等效于synchonized(Singleton.class).
一样会有问题。
27 楼
lefish
2008-07-10
这样可能会好一点.
private static HashCodeDigest instance = null; private HashCodeDigest() { } private static synchronized void syncInit() { if (instance == null) { instance = new HashCodeDigest(); } } public static HashCodeDigest getInstance() { if (instance == null) { syncInit(); } return instance; }
26 楼
kaipingk
2008-07-10
再集群环境下怎么实现单例模式?
25 楼
suntiance
2008-07-09
dennis_zane 写道
Lucas Lee 写道
你这种说法不知道有什么证据?
我以前也和你一样,想当然这样认为“对这个类的任何访问都会触发,比如这个类有什么全局常量的访问,也会触发初始化”,但我做了个小实验,SUN JDK1.6下(JDK1.5一样):
执行结果如下:
说明调用Singleton.NAME常量是不会触发此类其他静态变量的初始化的,(即单例也不会被初始化)。
调用静态方法则会。
我以前也和你一样,想当然这样认为“对这个类的任何访问都会触发,比如这个类有什么全局常量的访问,也会触发初始化”,但我做了个小实验,SUN JDK1.6下(JDK1.5一样):
package test; public class Singleton { public static final String NAME = "test"; private static final Singleton INSTANCE = new Singleton(); private Singleton() { System.out.println("Invoke constructor."); } public static Singleton getInstance() { return INSTANCE; } }
package test; public class Test1 { public static void main(String[] args) throws Exception { System.out.println(Singleton.NAME); System.out.println("-------------------"); Singleton.printString("Test static method."); } }
执行结果如下:
test ------------------- Invoke constructor. Test static method.
说明调用Singleton.NAME常量是不会触发此类其他静态变量的初始化的,(即单例也不会被初始化)。
调用静态方法则会。
static final会直接编译成常量,也就是Test1中的Single.NAME其实是直接初始化成常量,并没有调用到Singleton。看看这个帖子 http://www.iteye.com/topic/211937
是这样的,访问static final不属于主动调用。
24 楼
icewubin
2008-07-08
多谢举出反例,不过即使如此,之前说的lazy的程度不一样还是成立的。
没想到JVM对static final的常量优化的如此彻底啊。
没想到JVM对static final的常量优化的如此彻底啊。
发表评论
-
用Eclipse在Web项目引用Java项目时实现热部署
2007-03-09 11:28 18875以前也研究过Eclipse里Web Project引用Java ... -
PreparedStatement与Statement性能详细对比
2007-01-22 22:28 11806我对PreparedStatement和Statement的性 ... -
谨慎重写代码
2007-01-19 17:39 4930程序员很容易就会产生重写的冲动,因为推倒重来比维护一个现有系统 ... -
使用DBCP连接池检测未关闭的数据库连接
2006-11-17 14:22 8924我一直使用DBCP连接池,效果还不错。 最近因为朋友的一个J2 ... -
权限控制系统概述
2006-10-26 10:10 7114权限概念中,我认为原子元素只有三种:用户、功能权限、数据权限。 ... -
用Java编写Oracle存储过程
2006-10-24 15:17 25030Oracle里可以使用多种语言来编写存储过程,比如Pro*C/ ... -
Re: 你的系统是跨平台的吗?
2006-10-20 14:41 4231楼主总结得还不错。不过看上去似乎经验不足。(我说错了请原谅我) ... -
B/S是趋势和主流?
2006-10-12 16:40 8651我对B/S是趋势和主流这 ... -
Re: 复杂商品分类的表如何建立?
2006-10-12 12:38 4442zww80216 写道复杂商品的 ... -
实现数据库兼容并不象你想象的那么难
2006-10-12 10:58 5435ylt 写道 我同意Readonly的观点。如果不用hql,想 ... -
缺陷管理系统ClearBug发布
2006-09-18 16:41 8107看看网上流行的各种缺陷管理系统,总是觉得有所缺憾。 BugZi ... -
开源琴棋报表1.3.3发布
2006-09-18 16:24 16468轻量级。功能简明,易于使用,适用与中小型应用。 特别针对中国 ... -
Web服务器开发环境下的线程安全问题
2006-09-18 10:58 5701Servlet是在多线程环境下 ... -
MySQL5之使用经验
2006-09-18 10:57 6337MySQL5支持视图、存储过程、触发器等高级特性了,终于象个完 ... -
编程方法的发展
2006-09-18 10:56 7069我想了解一下软件开发中的编程方法的历史,以及现在流行的方法的来 ... -
如何删除字典数据?
2006-09-01 15:29 4406所谓字典就是数据库应用中被其他表(通常加以外键约束)引用的表, ...
相关推荐
项目经过测试均可完美运行! 环境说明: 开发语言:java jdk:jdk1.8 数据库:mysql 5.7+ 数据库工具:Navicat11+ 管理工具:maven 开发工具:idea/eclipse
项目经过测试均可完美运行! 环境说明: 开发语言:java jdk:jdk1.8 数据库:mysql 5.7+ 数据库工具:Navicat11+ 管理工具:maven 开发工具:idea/eclipse
基于java的网吧管理系统答辩PPT.pptx
基于java的基于SSM架构的网上书城系统答辩PPT.pptx
tornado-6.1-cp37-cp37m-win32.whl
c语言气泡排序、插入排序、选择排序、快速排序、希尔排序、堆排序、合并排序_SortAlgorithm.zip
Keyboard Maestro 11.0.3_macwk.dmg
基于微信小程序的鲜花销售微信小程序答辩PPT.pptx
项目经过测试均可完美运行! 环境说明: 开发语言:java jdk:jdk1.8 数据库:mysql 5.7+ 数据库工具:Navicat11+ 管理工具:maven 开发工具:idea/eclipse
tornado-6.2b1-cp39-cp39-musllinux_1_1_x86_64.whl
项目经过测试均可完美运行! 环境说明: 开发语言:java jdk:jdk1.8 数据库:mysql 5.7+ 数据库工具:Navicat11+ 管理工具:maven 开发工具:idea/eclipse
tornado-6.1b2-cp38-cp38-manylinux2014_aarch64.whl
基于java的土家风景文化管理平台答辩PPT.pptx
jira安装包
基于java的机场网上订票系统答辩PPT.pptx
小区物业管理系统 SSM毕业设计 附带论文 启动教程:https://www.bilibili.com/video/BV1GK1iYyE2B
yolo算法-金属-纸张-硬纸板垃圾数据集-13409张图像带标签-金属-纸张-硬纸板-塑料-其他-烟蒂-食物-玻璃.zip;yolo算法-金属-纸张-硬纸板垃圾数据集-13409张图像带标签-金属-纸张-硬纸板-塑料-其他-烟蒂-食物-玻璃.zip;yolo算法-金属-纸张-硬纸板垃圾数据集-13409张图像带标签-金属-纸张-硬纸板-塑料-其他-烟蒂-食物-玻璃.zip
项目介绍: 系统模块主要包括;用户、考试信息、考场信息、试卷、试题、考试等管理功能 环境说明: 开发语言:java jdk:jdk1.8 数据库:mysql 5.7+ 数据库工具:Navicat11+ 管理工具:maven 开发工具:idea/eclipse
项目经过测试均可完美运行! 环境说明: 开发语言:java jdk:jdk1.8 数据库:mysql 5.7+ 数据库工具:Navicat11+ 管理工具:maven 开发工具:idea/eclipse
Python脚本运行环境搭建所需要的资源包