线程安全和性能之间的博弈问题:
来源:互联网 发布:网络上1米是多少钱 编辑:程序博客网 时间:2024/06/05 07:27
多线程环境下,要保证资源数据安全性,同步锁的使用会带来性能问题。突出在singleton的使用上,lazy initalization不使用,就不会有这个问题,其实首先应考虑的是业务场景里该性能问题是不是一个性能需求
banq: static使用需要和多线程概念联系起来,过分使用Static会导致系统单线程运行,整个系统性能大幅度降低,使得你都无法定位性能问题。
我曾经碰到过一个游戏系统,由于大量使用static 和singleton,导致单线程,速度太慢了。注意:对于一些初学者,单线程开发是最方便的,这也是没有架构设计原因导致的,这样的项目必然失败。
robbin: static方法本身不会影响性能,笼统的来说,由于不需要new对象,也不需要隐式传递对象引用,因此内存消耗少,执行速度快。
但是由于static方法调用本身是不访问对象的,因此方法内部不能够使用非静态的属性,如果你不管三七二十一乱用static方法,必然会造成定义大量的静态属性,这样反而会造成内存消耗大,对象之间偶合性非常高的问题。
因此是否采用static方法不取决于内存消耗和执行效率,而是取决于你的业务逻辑。笼统的来说,如果对象是有状态的,那么需要new一个对象,方法不是静态的,如果对象是无状态的,那么使用静态方法,把类的静态方法当做函数调用来使用。
因为static方法内部不能够使用非静态的属性,所以用singleton替换
[补课]Singleton的性能问题
gigix: 最近我听到了这样一种说法: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模式能够提升系统的性能。
banq: 当多线程争用同一个共享资源对象时,才可能出现性能陷井,例如线程互锁。当然,你可以认为这不是Singleton的错,但是在多线程共享一个对象情况下,如果你对Singleton没有陷井认识,而且喜欢简单的话,总是会将这个对象做成Singleton(可以有其他做法),那么问题就来了。
1、Singleton 并不真正产生性能问题,只是需要注意使用Singleton 的时机,不要滥用。
2、只有当多线程争用同一个共享资源对象时,若要解决同步问题,才会产生性能问题,但这不是Singleton 对象的问题,任意对象都可能碰到。
banq: lazy initalization在实际中可以不用,因此回避这个问题,主要带来的不只是一点性能问题,而是可能变成非单态。
“Singleton 并不真正产生性能问题,只是需要注意使用Singleton 的时机,不要滥用。”
是这样,这个问题就象以前ClassLoader问题一样,ClassLoader本身不复杂,甚至程序员名人都对其字节码分析过(个人觉得这是一种典型的向下思维,我推荐搞应用的要有向上思维,注重技术的应用场景,这也是设计模式的要点),其实ClassLoader复杂问题是在其应用场景,如Weblogic/Jboss的EJB、WEB和Ear打包处理中。
所以我说Singletong应用有性能陷井,也是指其应用场景,用的时候要小心,不是一点陷井没有,是有的。其实,想起一个技术,就要立即想起它的应用场景,这才表面你已经有经验灵活运用它,(不同意:今后请不要一提Singleton就联想到性能有问题。它两之间没有必然联系。)因为任何技术都是有应用前提的。
Doug Lea:
Some one write the following program, he use HashMap to cache some values,You are correct that this is not thread-safe. If this cache canbe accrssed by mulitple threads, you should use a thread-safeMap such as the new java.util.concurrent.ConcurrentHashMap (or,just java.util.Hashtable)
现在有这样一个问题:有这么一个客户端,用户可以定阅世界上所有城市的温度信息,每5分钟客户端刷新一次,那么服务器端肯定要在内存中做个cache,我的想法是做个hashmap来存储,然后这个hashmap位于一个单例中,因为城市太多,而且有的城市定制的人很少,所以采用lazy initialization的方式,只有当第一次被访问的时候,才缓冲该城市信息,并且5分钟后该信息自动失效,看了gxixi的文章,这里只能用同步的方式,这样会显著影响效率,那么究竟应该怎么做那?
如果说几千个数据还可以一次全部初始化,那么如果数据多的多该真么办那?
不需要同步,又哪来的什么“性能问题”呢
private Map _tempCache; //HashMappublic Long getTemperature(String city) {
Long result = (Long) _tempCache.get(city);
if(result == null){
result = calcTemperature(city);
_tempCache.put(city, result);
}
return result;
}
虽然HashMap not thread safety
有并发又怎么样呢?并发冲突了又怎么样呢?有什么严重的后果?不妨看看代码:
if(!_cache.contains(city)) { // 现在还没这个城市的温度 // 突然这时候另一个线程访问了,cache里有这个城市的温度了_cache.put(calcTemperature(city)); // 于是,cache里刚放进去的数据被冲掉}
那又怎么样呢?无非是用下一秒钟的温度冲掉了上一秒的而已。除非真是《后天》的那种情况,否则我可以很放心地说,这两个温度值就是一样的――如果它们不一样,那也是温度传感器的问题。你根本不需要一秒钟以内的准确性,那你为什么要去保证写入的互斥?
fengyun:
我也从来没有听说过Singleton的性能问题。我们是用Java做邮件系统的,MTA都是用Java实现的,涉及到众多
多线程的程序,性能方面考虑的也不少。没有觉得Singleton会带来什么性能问题,如果说性能问题也只是说因为不合理的程序而导致的。
与Singleton没有关系。
一般我们性能优化遵循一些基本的准则:
1. 减少内存的开辟,复用提高效率。
2. 减少对象的构造,复用已经创建的对象。
3. 减少消耗时间的操作频率(采用cache等)
4. 用基于byte[]的操作来代替字符串的操作
....
根据上面的 2 3 原则来分析下面的程序:
这样的程序,如果大部分时间耗费在calcTemperature上,那么这个程序应该用下面的程序来代替:
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);
}
//替换程序
private Map _tempCache;
public Long getTemperature(String city)
{
//直接获取
Long result = _tempCache.get(city);
if (result == null) {
//如果不存在 Double Checking
synchronized(this) {
//因为内存操作比起计算或者从数据库获取快很多
result = _tempCache.get(city);
if (result == null) {
result = calcTemperature(city);
_tempCache.put(city, result);
}
}
}
return result;
}
- 线程安全和性能之间的博弈问题:
- 浅谈JAVA的线程安全与性能之间的权衡
- Servlet安全和性能问题
- tbb 线程安全concurrent_queue的性能
- SimpleDateFormat线程安全的问题
- iOS线程安全的问题
- 线程之间的通信(生产者和消费者问题)
- 消费者和生产者问题的实现-基于线程安全的容器来和非线程安全的容器类
- 两个线程之间的同步(生产者与消费者问题)性能优化
- 线程之间和进程之间的同步
- 线程安全和线程不安全的区别
- 线程安全和线程不安全的区别
- 考虑性能和线程安全的java单例模式实现
- C++ 单例模式,考虑线程安全和性能的几种方式
- 非线程安全的HashMap 和 线程安全的ConcurrentHashMap
- JAVA的线程安全和非线程安全的区别
- 笔面中公司和牛人之间的博弈
- 线程安全的理解和如何保证线程安全
- OpenGL入门学习(五)
- 获取IE历史记录
- 快速排序List的通用方法
- 黑马程序员_Java学习日记12_IO流1
- js判断变量是否未定义(转)
- 线程安全和性能之间的博弈问题:
- 学习《21天学通Java 6》(四)第3章总结
- VS2010与资源文件的关联
- ios学习笔记之字典(NSDictionary)对象相应方法的用途
- JDK源码分析之String
- OpenGL入门学习(六)
- 软考--信息系统项目管理师问答(不断更新整理中)
- OpenGL入门学习(七)
- 黑马程序员_Java学习日记14_IO流3