- 浏览: 206013 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
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 18880以前也研究过Eclipse里Web Project引用Java ... -
PreparedStatement与Statement性能详细对比
2007-01-22 22:28 11809我对PreparedStatement和Statement的性 ... -
谨慎重写代码
2007-01-19 17:39 4938程序员很容易就会产生重写的冲动,因为推倒重来比维护一个现有系统 ... -
使用DBCP连接池检测未关闭的数据库连接
2006-11-17 14:22 8932我一直使用DBCP连接池,效果还不错。 最近因为朋友的一个J2 ... -
权限控制系统概述
2006-10-26 10:10 7121权限概念中,我认为原子元素只有三种:用户、功能权限、数据权限。 ... -
用Java编写Oracle存储过程
2006-10-24 15:17 25035Oracle里可以使用多种语言来编写存储过程,比如Pro*C/ ... -
Re: 你的系统是跨平台的吗?
2006-10-20 14:41 4238楼主总结得还不错。不过看上去似乎经验不足。(我说错了请原谅我) ... -
B/S是趋势和主流?
2006-10-12 16:40 8653我对B/S是趋势和主流这 ... -
Re: 复杂商品分类的表如何建立?
2006-10-12 12:38 4450zww80216 写道复杂商品的 ... -
实现数据库兼容并不象你想象的那么难
2006-10-12 10:58 5438ylt 写道 我同意Readonly的观点。如果不用hql,想 ... -
缺陷管理系统ClearBug发布
2006-09-18 16:41 8116看看网上流行的各种缺陷管理系统,总是觉得有所缺憾。 BugZi ... -
开源琴棋报表1.3.3发布
2006-09-18 16:24 16483轻量级。功能简明,易于使用,适用与中小型应用。 特别针对中国 ... -
Web服务器开发环境下的线程安全问题
2006-09-18 10:58 5704Servlet是在多线程环境下 ... -
MySQL5之使用经验
2006-09-18 10:57 6346MySQL5支持视图、存储过程、触发器等高级特性了,终于象个完 ... -
编程方法的发展
2006-09-18 10:56 7076我想了解一下软件开发中的编程方法的历史,以及现在流行的方法的来 ... -
如何删除字典数据?
2006-09-01 15:29 4415所谓字典就是数据库应用中被其他表(通常加以外键约束)引用的表, ...
相关推荐
单例模式是一种设计模式,旨在确保一个类只有一个实例,并提供全局访问点。在单例模式中,类的构造函数是私有的,防止外部直接创建对象,而是通过静态方法获取该类的唯一实例。单例模式的唯一性通常是在进程范围内,...
"设计模式单例模式和工厂模式综合应用"的主题聚焦于两种常用的设计模式:单例模式和工厂模式,并探讨它们如何协同工作来实现高效、灵活的代码结构。这个主题尤其适用于Java编程语言,因为Java的面向对象特性使得设计...
单例模式是软件设计模式中的一种经典模式,它保证了类只有一个实例存在,并提供一个全局访问点。在Java等面向对象编程语言中,单例模式常用于管理共享资源,如数据库连接池、线程池或者配置文件等。结合工厂模式,...
单例模式是软件设计模式中的一种,它保证一个类只有一个实例,并提供一个全局访问点。在C#中,单例模式常用于管理共享资源或控制类的实例化过程,以提高性能、节约系统资源,特别是在整个应用程序生命周期内只需要一...
单例模式是软件设计模式中的一种经典模式,用于确保一个类只有一个实例,并提供一个全局访问点。在Java中,有多种实现单例模式的方法,每种都有其特点和适用场景。接下来,我们将深入探讨这些实现方式。 首先,我们...
在C++编程中,单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供一个全局访问点。线程安全的单例模式在多线程环境下尤其重要,因为不正确的实现可能导致多个线程创建多个实例,这违反了单例模式...
**设计模式——单例模式** 在软件工程中,设计模式是一种在特定场景下解决常见问题的标准方案,可以被复用并提升代码质量。单例模式是设计模式中的一种,它保证一个类只有一个实例,并提供一个全局访问点。这种模式...
Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式...
单例模式是软件设计模式中的一种经典模式,其主要目的是确保一个类只有一个实例,并提供一个全局访问点。这种模式在很多场景下非常有用,比如控制共享资源、管理配置对象等。下面将详细介绍七种常见的单例模式实现...
单例模式是一种设计模式,它的主要目标是确保一个类只有一个实例,并提供一个全局访问点。在软件工程中,单例模式常用于控制资源的共享,比如数据库连接池...理解并正确使用单例模式对于构建高效、稳定的系统至关重要。
在C++编程中,单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供一个全局访问点。在这个特定的场景中,我们讨论的是一个实现了单例模式的日志类,该类专为多线程环境设计,具备日志等级控制、...
单例模式是软件设计模式中的一种,它的主要目的是确保一个类只有一个实例,并提供一个全局访问点。这种模式在很多场景下都非常有用,比如控制资源的唯一性、管理共享配置或者创建昂贵的对象时避免频繁创建销毁。 ...
Java中的单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供全局访问点。在Java编程中,单例模式常用于控制资源的访问,比如数据库连接池、线程池或者日志对象等。本篇文章将深入探讨如何在Java中...
由于该工具对于操作系统来说是独一无二且不可重复开启的,因此它符合单例模式的核心特点——确保任何时候都只有一个实例存在。这不仅简化了用户的操作体验,也提高了系统的稳定性和安全性。 **2. Windows Recycle ...
单例模式是软件设计模式中的经典模式之一,其主要目的是控制类的实例化过程,确保在应用程序的整个生命周期中,某个类只有一个实例存在。这样的设计通常适用于那些需要频繁创建和销毁,但资源消耗较大的对象,如...
首先向关注过我这个系列...这立刻让我想到了最常用也是最简单最容易理解的一个设计模式 单例模式 何为 单例模式 ? 故名思议 即 让 类 永远都只能有一个实例。 由于 示例代码 比较简单 我也加了注释,这里就不在赘述
单例模式是软件设计模式中的一种,它的主要目的是确保一个类只有一个实例,并提供一个全局访问点。在Java或类似的面向对象编程语言中,单例模式常用于管理共享资源,如数据库连接池、线程池或者配置文件等。在这个...
单例模式是软件设计模式中的一种基础且广泛应用的模式,它的主要目的是确保一个类只有一个实例,并提供一个全局访问点。这种模式在系统中需要频繁创建和销毁对象,且对象创建成本较高,或者需要共享资源的情况下非常...
### 连接数据库单例模式解析 在软件开发过程中,特别是在涉及到数据库操作的应用程序中,合理地管理和控制数据库连接是非常重要的。不当的数据库连接管理可能会导致资源浪费、性能下降甚至是系统崩溃等问题。为了...