这个问题的讨论来自内部的一个关于“多线程环境下使用Hashmap的安全问题”的讨论,HashMap多线程的问题之前已经提过一次,见之前的blog
.本篇文章主要讨论多线程下race condition的问题。以下内容部分引用自内部邮件:
错误代码:
定义成员变量
private
static
Map cachedMap = new
HashMap(7000);
private
static
Boolean firstInvoke = true;
程序是设想在第一次开始对该map变量进行初始化
线程1:
Public Object getMyValue(){
If(firstInvoke){
While(i<7000){
…………
cachedMap.put("new"
,"newValue"
);
i++;
}
firstInvoke = false;
}
}
线程2:
在线程1对cachedMap对象put的时候,线程2从这个cachedMap中取值
cachedMap.get("new"
);
错误分析
单步Debug是没问题,但代码在多线程情况下工作会出现线程安全。
Hashmap不是读写线程安全的,只有全部只读才是线程安全的,Hashmap在被并发读写使用的时候会出现线程安全问题,一般理解的线程安全问题导致
的是数据错误。 而Hashmap多线程同时读写操作时,可能使程序挂起。
以下引用自http://sdh5724.javaeye.com/blog/619130
分析: 我们知道Hashmap在被并发读写使用的时候,
会抛出ConcurrentModificationException这个异常, 但是JDK文档明确指出, 这个异常抛出是属于 fail-fast
的一个设计方法, 目的是为了开发者能及早的意识到线程安全问题发生。 但是, 这个fail-fast不是一定会发生, 而是可能会发生的行为。
因此, 在一个不确定状态下的下,jvm线程发生持续100%cpu行为是比较容易理解了(for (Entry<K,V> e =
table[i]; e != null; e = e.next), 目前只能估计是这个代码进入死循环的状态,还不能非常明确)。
“正确用法”
注意更改HashMap中的内容时是否存在同时并发线程读的情况,如果有, 需要对读写的入口做同步. 如果知道要在多线程情况下读写Map,
建议使用线程安全的ConcurrentHashMap实现代替HashMap。ConcurrentHashMap
可以在不损失线程安全的同时提供很好的并发性。
代码如下:
private
static
Map cacheMap = new
ConcurrentHashMap(7000);
private
static
Boolean firstInvoke = true;
程序是设想在第一次开始对该map变量进行初始化
线程1:
Public Object getMyValue(){
If(firstInvoke){
While(i<7000){
…………
cachedMap.put("new"
,"newValue"
);
i++;
}
firstInvoke = false;
}
}
线程2:
在线程1对cachedMap对象put的时候,线程2从这个cachedMap中取值
cachedMap.get("new"
);
上述解决方案的race condition
问题:
这个HashMap不当使用的问题很经典。很多时候我们用“单线程”思维习惯去写代码,不知不觉就忘记了运行时的多线程场景。
其实,我觉得下面的例子中还是有隐含的race condition问题的,那就是在这个if(firstInvoke) then load data and firstInvoke=false这个逻辑中。
即:If(firstInvoke){…
//ß 这里可能会导致多条线程同时进入,导致多次load data
通常我们用一个boolean变量来实现lazy操作, 那么在多线程环境下,要记得使用synchronize关键词 或者 采用volatile类型变量+CAS操作,确保变量被每条线程都能正确的读取和写入。
1.
保险的做法
:(在最新JVM中,这种方式是最安全,最可读,性价比最高的,如果JVM支持锁逃逸即Biased Locking
,性能也会非常好)
Synchronized(lock){
If(firstInvoke){
Then load data…
firstInvoke = false
}
}
2.
或者
,用volatile变量+DCL
Private volatile
boolean
firstInvoke = true;
If(firstInvoke){
Synchronized(lock){
If(firstInvoke){
Then load data …
firstInvoke = false;
}
}
}
3.
SMP
友好,但是偷懒的做法
,用AtomicBoolean,里面用到了CompareAndSet操作。(volatile只保证变量可见性,Spinning CAS保证操作原子性)
Private AtomicBoolean firstInvoke = new
AtomicBoolean(true);
If(firstInvoke.getAndSet(false)){ // cas spinning inside the AtomicBoolean::getAndSet() method
Then load data…
}
4.
最后,最复杂,但是同时满足SMP友好,及性能最佳的:
private
AtomicBoolean firstInvoke = new
AtomicBoolean(true);
for
(;;){
Boolean current = firstInvoke.get();
If(!current){ // the most likely condition branch, see http://pt.alibaba-inc.com/wp/dev_related/optimization_363/likely-unlikely.html
Break;
}
If(firstInvoke.compareAndSet(current,false){
Then load data…
Break;
}
}
在××××代码中,为了确保SMP状态下性能最优,我们在某一些关键地方也用到了上面的CAS+spinning的技巧。
我们也许并不会时时刻刻用到“回字的四种写法”,但是搞清楚JVM内存可见性和操作原子性的基本概念还是必须的,这也是确保写出线程安全代码的前提条件)。
参考资料:
http://sdh5724.javaeye.com/blog/619130
http://www.tech-faq.com/race-condition.html
《 The Art of Multiprocessor Programming》 http://book.douban.com/subject/3024605/
race condition by @Shawn
相关资料:
Java轻量级锁原理详解(Lightweight Locking)
Java偏向锁实现原理(Biased Locking)
深入理解DCL(双检锁)的安全性
分享到:
相关推荐
线程并发控制condition 互斥量 多线程写的:生产者、消费者问题
多线程同步解决卖票问题
c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程c_多线程...
多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程多线程
线程通信(Condition)实例,完整的代码文件,你只需要编译运行即可,就可以看看结果,然后分析。
b: 创建多个线程 c: 多线程访问同一资源 d: 经典线程同步互斥问题 e: 使用关键段解决子线程互斥问题 f: 利用事件实现线程同步问题 g: 利用互斥量来解决线程同步互斥问题 h: problem1 生产者消费者问题 (1...
Linux系统下的多线程编程入门.pdf Linux系统下的多线程编程入门.pdf Linux系统下的多线程编程入门.pdf Linux系统下的多线程编程入门.pdf Linux系统下的多线程编程入门.pdf Linux系统下的多线程编程入门.pdf Linux...
多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子多线程列子
串口通信的实现,编程环境为C#,实现技术采用了多线程方式
C#_细说多线程(上下)
这是陈硕在 2009 年上海 C++ 技术大会演讲...如何避免这种 race condition 是 C++ 多线程编程面临的基本问题,可以借助 tr1 中的 shared_ptr 和 weak_ptr 完美解决。这也是实现线程安全的 Observer 模式的必备技术。
本资源描述了C++11 中多线程的创建,C++11中std命名空间中将boost库中的Thread加入,boost多线程从准标准变为标准,其中还介绍了C++ 多线程下的单例模式的使用,本文档为txt文档
Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式...
多线程聊天多线程聊天多线程聊天多线程聊天多线程聊天多线程聊天多线程聊天多线程聊天多线程聊天多线程聊天多线程聊天多线程聊天
java多线程PPT 多线程基本概念 创建线程的方式 线程的挂起与唤醒 多线程问题
本书是一本通俗易懂的C#多线程编程指南,通过70多个容易理解的示例,循序渐进地讲解C#5.0中的异步及并发编程,引导读者了解Windows下C#多线程编程的多样性。 通过阅读本书,你将学到: 使用原始线程、异步线程,...
总结了一下Qt中sqlite多线程操作遇到的几个问题,希望能对有需要的朋友一点帮助 总结了一下Qt中sqlite多线程操作遇到的几个问题,希望能对有需要的朋友一点帮助
Fleck.dll支持websocket引自博客文章多线程下的websocket实时通信,具体使用方式请看文章多线程下的websocket实时通信,包括具体的服务器端跟客户端编码。
C#.NET多线程实例6个(包括多线程基本使用,多线程互斥等全部多线程使用实例),可直接运行
C# 多线程实例C# 多线程实例C# 多线程实例C# 多线程实例C# 多线程实例C# 多线程实例