JiveJdon Community Forums
在线71人   首页   主题总表   培训咨询   精华   查搜   注册    登陆
首页 » 论坛 » Java多线程 集群 并行模式
???en_US.forumThreadPrev.name??? 上一主题
  Go back to the topic 返回本主题   Go back to the topic listing返回主题列表
???en_US.forumThreadNext.name??? 下一主题
1 2 3 4 ... 5 Go 总共有 62 回复 / 5
 发表新帖子   回复该主题贴
anonymous

悄悄话
发表文章: 0
注册时间:
[补课]Singleton的性能问题 2004年10月19日 10:38 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
标签列表 singleton模式(35)      设计模式(142)     
最近我听到了这样一种说法:Singleton模式在多线程环境下存在性能问题。并且,这就成了Singleton模式的一个罪状:因为Singleton在多线程底下有性能问题,因为J2EE是多线程的,所以J2EE底下不应该用Singleton模式。现在我来帮这些擅长过度省略的高手们补补课:Singleton模式,究竟在怎样的情况下才有性能陷阱?

第一个问题,Singleton模式在多线程环境为何遭遇困境?答案是,采用lazy initialization策略时,如果没有合理的同步(synchronize),各个线程得到的实例可能不是同一个。详情可以参考JavaWorld 2001年的文章:When Is A Singleton Not A Singleton。(某些同志连这篇文章都没听说过,居然也可以抱怨说“找不到这方面的英文资料”,恩,我们说话恐怕还是谦虚谨慎点好。)


public class MyClass {
private static MyClass _instance;
public static MyClass getInstance() {
if(_instance != null) {
_instance = new MyClass();
}
return _instance;
}


如果有两个线程同时调用MyClass.getInstance()方法,就有可能造成MyClass的构造子被调用两次。所以我们需要同步――准确说,恰当的同步。在C++里面有一种常见的Singleton实现策略叫Double Checked Locking idiom(http://www.javaworld.com/javaworld/jw-01-2001/jw-0112-singleton_p.html,listing 6),但这种实现策略在Java中不生效(这是由于JVM的本性造成的,详情请看这篇文章:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html ),因此如果要在Java中实现lazy initialization策略的Singleton,你就必须采取保守的同步策略,也就是:



public static [b]synchronized[/b] MyClass getInstance() {
...
}


如果采取保守的同步策略(将整个getInstance()方法同步),多个线程需要获得Singleton实例时就必须在getInstance()方法上排队等待。这就是传说中的“Singleton模式的性能问题”。现在我要提问了:这种性能问题在什么情况下才会出现?

答案就摆在你面前:只有采用lazy initialization策略时,才会存在这样的性能问题。那么如果放弃lazy initialization策略、改用eager initialization策略(即:预先创建好Singleton实例),Singleton模式还会存在这样的性能问题吗?我们把上面的例子改成eager initialization策略看看:


public class MyClass {
private static MyClass _instance = [b]new MyClass()[/b];
public static MyClass getInstance() {
return _instance;
}


我请问,这样的一个Singleton难道还会有什么“性能问题”吗?它付出的代价是更长的初始化时间,获得的收益则是更快并且线程安全的实例获得,而这正是Spring容器对其管理的组件的默认策略。其实这个问题早已有了定论,请看http://www.javaworld.com/javaworld/jw-01-2001/jw-0112-singleton_p.html这篇文章的listing 1和listing 2,Singleton模式的两种正确的实现策略早在2001年就已经讨论清楚了。

作为结论,我提醒某些善于过度简化乃至以讹传讹的高手们:请不要简单地说一句“Singleton模式有性能问题”了事。完整的说法应该是,当采用Lazy Initialization策略时,如果需要经常地获取Singleton实例,则Singleton模式中用于获取实例的方法有可能成为性能瓶颈;如果条件允许采用Eager Initialization策略,则Singleton模式不会带来任何额外的性能开销――如果考虑管理对象池或是新建对象实例的性能开销,Singleton模式能够提升系统的性能。
robertfang

悄悄话
发表文章: 14
注册时间: 2004年10月25日 22:22
Re: [补课]Singleton的性能问题 2004年10月25日 22:23 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
现在有这样一个问题:有这么一个客户端,用户可以定阅世界上所有城市的温度信息,每5分钟客户端刷新一次,那么服务器端肯定要在内存中做个cache,我的想法是做个hashmap来存储,然后这个hashmap位于一个单例中,因为城市太多,而且有的城市定制的人很少,所以采用lazy initialization的方式,只有当第一次被访问的时候,才缓冲该城市信息,并且5分钟后该信息自动失效,看了gxixi的文章,这里只能用同步的方式,这样会显著影响效率,那么究竟应该怎么做那?
如果说几千个数据还可以一次全部初始化,那么如果数据多的多该真么办那?
anonymous

悄悄话
发表文章: 0
注册时间:
Re: [补课]Singleton的性能问题 2004年10月25日 23:58 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
>
>
> 现在有这样一个问题:有这么一个客户端,用户可以定阅世界
> 纤谐鞘械奈露刃畔?,每5分钟客户端刷新一次,那么服务器端
> 隙ㄒ谀诖嬷凶龈cache,我的想法是做个hashmap来存储,然?
> 这个hashmap位于一个单例中,因为城市太多,而且有的城市定?
> 的人很少,所以采用lazy
> initialization的方式,只有当第一次被访问的时候,才缓冲该
> 鞘行畔?,并且5分钟后该信息自动失效,看了gxixi的文章,这里
> 荒苡猛降姆绞?,这样会显著影响效率,那么究竟应该怎么做?
> ?
>
> 如果说几千个数据还可以一次全部初始化,那么如果数据多的
> 喔谜婷窗炷??

你想清楚自己究竟要做什么了吗?


private Map _tempCache;
public Long getTemperature(String city) {
if(!_tempCache.contains(city)) {
Long result = calcTemperature(city);
_tempCache.put(city, result);
}
return (Long) _tempCache.get(city);
}


请问你,这里有任何一个地方需要同步吗?如果压根不需要同步,又哪来的什么“性能问题”呢?
robertfang

悄悄话
发表文章: 14
注册时间: 2004年10月25日 22:22
Re: [补课]Singleton的性能问题 2004年10月26日 09:41 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
你把这个问题避开了,你的MAP是什么?hashtable本身就是同步的,用在这里本身就是同步性能陷阱。
用hashmap,完全没有同步问题,可是无法保证1读写同一个键的互斥性,2会出现两个线程并发给一个key赋值得现象,如果值不是简单的long而是复杂的数据库查询结果的话,还是有很大隐患的。
我觉得解决方案还在这篇文章上,
http://www-900.ibm.com/developerWorks/cn/java/j-jtp07233/

或许你有什么更好的方法?
于诸君共勉:)

anonymous

悄悄话
发表文章: 0
注册时间:
Re: [补课]Singleton的性能问题 2004年10月26日 10:29 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
>>用hashmap,完全没有同步问题,可是无法保证1读写同一个键的互斥性,2会出现两个线程并发给一个key赋值得现象,如果值不是简单的long而是复杂的数据库查询结果的话,还是有很大隐患的。

照这个需求,“温度信息”是每过5分钟才更新一次的,我为什么要去保证它一秒钟内两次写操作互斥?如果说同一瞬间有两个请求过来了,很好,由它们去吧,那又怎么样呢?这个需求根本就用不着保证什么同步性互斥性,为什么要强加给它呢?
robertfang

悄悄话
发表文章: 14
注册时间: 2004年10月25日 22:22
Re: [补课]Singleton的性能问题 2004年10月26日 11:30 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
照这个需求,“温度信息”是每过5分钟才更新一次的
-------------------------------------------------
客户端5分钟刷新一次,服务器端就有可能每时每刻都在接受访问,很有可能出现 并发阿


我为什么要去保证它一秒钟内两次写操作互斥?如果说同一瞬间有两个请求过来了,很好,由它们去吧,那又怎么样呢?
--------------------------------
如果是put 数据库查询读来的value object list,put 的时间比较长,对象比较复杂 ,就会发生写-写冲突阿 这可以由他去么? 同理 读-写冲突你怎么解决? 你的意思好像是不管?即使是long这样的简单数据,也有可能有问题
所以觉得还要考虑同步互斥

anonymous

悄悄话
发表文章: 0
注册时间:
Re: [补课]Singleton的性能问题 2004年10月26日 11:53 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
>>客户端5分钟刷新一次,服务器端就有可能每时每刻都在接受访问,很有可能出现 并发阿

有并发又怎么样呢?并发冲突了又怎么样呢?有什么严重的后果?不妨看看代码:


if(!_cache.contains(city)) { // 现在还没这个城市的温度
// 突然这时候另一个线程访问了,cache里有这个城市的温度了
_cache.put(calcTemperature(city));
// 于是,cache里刚放进去的数据被冲掉
}


那又怎么样呢?无非是用下一秒钟的温度冲掉了上一秒的而已。除非真是《后天》的那种情况,否则我可以很放心地说,这两个温度值就是一样的――如果它们不一样,那也是温度传感器的问题。你根本不需要一秒钟以内的准确性,那你为什么要去保证写入的互斥?
robertfang

悄悄话
发表文章: 14
注册时间: 2004年10月25日 22:22
Re: [补课]Singleton的性能问题 2004年10月26日 14:28 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
那又怎么样呢?无非是用下一秒钟的温度冲掉了上一秒的而已。

---------------------------
觉得你潜意识里仍然认为put是顺序执行的,真的是下冲掉上一次么
不是的,是两次并发执行,有可能是第一个long的几个字节和第二个long里面的几个字节组合成一个从来没有出现过的数据,
这是写写冲突,同理如果读了几个字节,写线程写入,读线程读取剩下的,读出的数据肯定不是其中的任何一个,


robertfang

悄悄话
发表文章: 14
注册时间: 2004年10月25日 22:22
Re: [补课]Singleton的性能问题 2004年10月26日 14:41 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
也许用温度的例子不太合适,OK,你也说了会发生并发冲突,

并发冲突了又怎么样呢?有什么严重的后果?
----------------
如果没后果我们也不用费劲搞什么同步互斥了

如果put 的是对象,可能这个对象二进制代码回是两个对象的混合体,结果谁也不认识,这还不严重么
anonymous

悄悄话
发表文章: 0
注册时间:
Re: [补课]Singleton的性能问题 2004年10月26日 14:41 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
>>不是的,是两次并发执行,有可能是第一个long的几个字节和第二个long里面的几个字节组合成一个从来没有出现过的数据

这不是滑天下之大稽吗?你放进map的是什么?是一个对象的句柄。难道对象句柄会因为缺乏同步而变成无效的?那你应该去提交JDK的bug,而不是在这个论坛提这种问题。
robertfang

悄悄话
发表文章: 14
注册时间: 2004年10月25日 22:22
Re: [补课]Singleton的性能问题 2004年10月26日 17:24 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
你的意思是hashmap的value存取是线程安全的了?
anonymous

悄悄话
发表文章: 0
注册时间:
Re: [补课]Singleton的性能问题 2004年10月26日 19:34 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
> 你的意思是hashmap的value存取是线程安全的了?

当然不是,JDK文档写得清清楚楚,HashMap不是线程安全的,HashTable才是。但请你搞搞清楚,“线程不安全”究竟是什么意思。它的意思是,如果两个写操作并发进去了,你将无法确保两个操作结束后得到的是后一次操作的结果。Java有可能像你说的那样,因为并发问题把对象句柄搞坏掉吗?32位整数操作天生是原子的,这在Java语言规范里面写得清清楚楚,你完全可以先看了再说,做技术的那么多臆测不是好事。
robertfang

悄悄话
发表文章: 14
注册时间: 2004年10月25日 22:22
Re: [补课]Singleton的性能问题 2004年10月26日 22:29 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
Ok 引用的问题是我的疏忽,可是你也说了hashmap不是线程安全的,你的put(key,value)是安全的么?答案是未必,也就是说put未必是32位操作,这你同意吧?jdk文档上都有

那么还是那个问题,你的put(city,long)是不安全的,因为city不一定存在,所以还是要保持读写,写写互斥吧?

我的问题是这样的:如果要保持写写,读写互斥,但不要读读互斥,hastable 不符合要求 hashma有什么办法让他可以符合这个要求?
anonymous

悄悄话
发表文章: 0
注册时间:
Re: [补课]Singleton的性能问题 2004年10月26日 23:05 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
>> 可是你也说了hashmap不是线程安全的,你的put(key,value)是安全的么?答案是未必,也就是说put未必是32位操作,这你同意吧?jdk文档上都有

我发现很难跟你沟通,因为你连基本的常识都还缺乏。我已经说过了,HashMap的“线程不安全性”是指:如果有两个并发写操作进入,不能保证整体的结果是后一操作的结果。譬如一个空的HashMap,如果两个并发调用下列语句:


//假设下列两个语句并发调用
map.put(
"key", "value1");
map.put(
"key", "value2");


将无法保证map.get("key")得到"value1"或是"value2",这是线程不安全的意义。至于你担心的“对象句柄被破坏”的问题,我请问你,put方法做什么事?不过是把一个对象的引用放到map分配的空间里而已。对象的引用是什么?不就是一个32位整数吗?如果说Java连并发环境下的对象句柄操作都不能保证完整性,它还算哪门子的“高级语言”?你还真把James Gosling当白痴啊?

下次请先补好了基础再来谈论应用问题,不要在讨论过程中还要不停地给你补习这些大学本科一年级的常识,这种讨论太累。
zxc005

悄悄话
发表文章: 5
注册时间: 2007年01月23日 17:58
有话儿好好说 2004年10月27日 06:29 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
有话儿好好说有话儿好好说有话儿好好说

从讨论中我还真有收获耶,赫赫……
这个主题有 62 回复 / 5Go 1 2 3 4 ... 5
???en_US.forumThreadPrev.name??? 上一主题
  Go back to the topic 返回本主题   Go back to the topic listing返回主题列表    返回页首返回页首
???en_US.forumThreadNext.name??? 下一主题
热点TAG: AOP cache 缓存 DDD EJB 集群 设计模式 Hibernate IOC JiveJdon OO RBAC Seam Spring Struts
google yahoo 新浪ViVi 365Key网摘 天极网摘 CSDN网摘 添加到百度搜藏 POCO网摘 博采网摘
查询本论坛内 回复超过的热门帖子
     回复该主题贴
标题
 
粗体 斜体 下划线 插入图片 插入代码 插入url链接 插入附件
内容
 

手机阅读 add to google add to yahoo
解惑之道在J道 ,打造中国最具影响力的的企业软件社区
OpenSource JIVEJDON v3.0 Powered by JdonFramework Code © 2002-08 jdon.com
anti spam