这个问题的讨论来自内部的一个关于“多线程环境下使用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(双检锁)的安全性
分享到:
相关推荐
竞争条件漏洞是一种在多线程或并发环境中常见的软件安全问题,它发生在多个线程或进程同时访问共享资源时,由于执行顺序的不确定性导致了不可预测的行为。这种问题可能导致数据丢失、数据不一致、程序崩溃甚至恶意...
然而,多线程编程也带来了一些挑战,尤其是当多个线程同时访问共享资源,如全局变量时,可能会出现竞态条件(Race Condition)和其他并发问题。本文将深入探讨标题“多线程同时操作全局变量的出错演示”所涉及的知识...
然而,多线程也带来了一些挑战,比如竞态条件(race condition)、死锁(deadlock)和资源争抢等问题。这些问题需要通过适当的同步机制来解决。例如,Java提供了`synchronized`关键字,C++提供了互斥量(mutex)和...
- 在多线程环境中,多个线程访问共享资源时可能会出现竞争条件(Race Condition)。 - 本例中,虽然没有明确使用同步机制如`synchronized`关键字或`Lock`接口等来确保线程安全,但在实际开发中应当考虑此类问题。 ...
调试多线程程序往往比单线程更复杂,因为线程行为的非确定性可能导致难以复现的问题。学会使用工具,如gdb的线程查看功能,以及编写可调试的多线程代码,是提高开发效率的重要技能。 总之,《POSIX多线程程序设计》...
在Java编程中,多线程是并发处理任务的关键技术,特别是在服务器端开发和高并发场景下。本篇文章将深入探讨“最简单的线程安全问题”,并结合相关源码和工具来帮助理解。线程安全问题通常涉及到多个线程对共享资源的...
在这个程序中,可能会使用这些同步工具来确保安全的数据交互,防止竞态条件(Race Condition)。 此外,可能还涉及到了线程的生命周期管理,包括启动线程(Thread.Start)、线程的优先级设置(Thread.Priority)、...
例如,"Race Conditions"(竞态条件)是多线程编程中常见的问题之一。当两个或更多线程访问共享资源并且它们的执行顺序影响结果时,就可能出现竞态条件。在上面的示例中,如果`thread_function`也访问了全局变量,...
然而,多线程也带来了一系列挑战,其中最突出的就是线程安全问题。线程安全是指在多线程环境中,程序能够正确地管理共享资源,避免数据不一致、死锁等错误。C#提供了多种机制来支持线程间的同步和通信,以确保线程...
其中最常见的问题是**竞态条件(Race Condition)**,即多个线程同时访问并修改同一数据,导致结果不可预测。另一个问题是**死锁(Deadlock)**,即两个或更多线程相互等待对方释放资源,导致所有线程都无法继续执行。 ...
- **线程安全的编程**:使用互斥量、原子操作、线程局部存储等技术避免数据竞争,确保多线程环境下的正确性。 通过理解并掌握这些知识点,开发者可以在VC++6.0环境中有效地编写多线程程序,实现高效的并发执行,...
但考虑到描述中提到了电子书的标题以及它涉及的主题,我们可以从标题“Java多线程”出发,详细阐述Java多线程编程的核心概念、技术细节、应用场景和常见问题处理。 Java多线程是一种允许多个线程在单个Java程序中...
本案例中,我们将学习如何使用C#语言来解决多线程问题,特别是解决i++操作符在多线程环境下的问题。 多线程编程是C#编程语言的重要特性,它允许程序同时执行多个任务来提高程序的执行效率。然而,在多线程编程中,...
在多线程环境中,数据竞争(race condition)是常见的问题。在"Race"示例中,可能每个线程代表一个赛跑者,他们都在试图更新同一个比赛结果。为避免数据不一致,我们需要使用同步机制,如互斥量(mutex)、信号量...
3. **并发控制**:由于多线程可能导致竞态条件(race condition)和死锁等问题,因此在并发操作中,可能需要使用Monitor、Mutex、Semaphore等同步原语来确保线程安全。在发送邮件时,可能需要确保同一时间只有一个...
然而,这也带来了一定的风险,即数据竞争(race condition),当两个或更多线程同时访问并修改同一资源时,可能会导致不确定的结果。 在C++中,自C++11标准开始,标准库提供了`<thread>`头文件来支持多线程编程。...
在IT领域,多线程编程是一项重要的技术,它允许程序同时执行多个任务,极大地提高了效率。在这个"多线程赛马游戏"中,我们...通过实现这个游戏,开发者可以深入理解多线程编程的核心概念,并锻炼解决并发问题的能力。
多线程同步技术的主要目的是解决线程间共享数据时可能出现的竞态条件(race condition),从而避免数据不一致的问题。在Java中,多线程同步可以通过synchronized关键字实现,该关键字可以用来修饰方法或代码块,确保...
在多线程环境中,由于线程间的异步行为,可能会出现竞态条件(race condition)和死锁(deadlock)等问题。因此,良好的错误处理机制和异常处理策略是必不可少的,以确保程序在遇到问题时能够优雅地恢复或终止。 ...
这里我们探讨的“Lock及多线程ATM取款实例”就是一个典型的多线程编程问题,主要涉及C#编程语言,以及如何在多线程环境下确保数据一致性与安全性。 首先,我们要理解什么是`Lock`。在C#中,`Lock`是一个关键字,...