这个问题的讨论来自内部的一个关于“多线程环境下使用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多线程同时读写操作时,可能使程序挂起。

分析: 我们知道Hashmap在被并发读写使用的时候, 会抛出ConcurrentModificationException这个异常, 但是JDK文档明确指出, 这个异常抛出是属于 fail-fast 的一个设计方法, 目的是为了开发者能及早的意识到线程安全问题发生。 但是, 这个fail-fast不是一定会发生, 而是可能会发生的行为。 因此, 在一个不确定状态下的下,jvm线程发生持续100%cpu行为是比较容易理解了(for (Entry

“正确用法”

注意更改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内存可见性和操作原子性的基本概念还是必须的,这也是确保写出线程安全代码的前提条件)。

参考资料:

《 The Art of Multiprocessor Programming》 http://book.douban.com/subject/3024605/

相关资料:

java race condition_java 多线程下race condition问题相关推荐

  1. java.text.SimpleDateFormat多线程下的问题

    1. 今天在做性能压测的时候发现java.text.SimpleDateFormat多线程下的错误 2. 先贴出两段错误大家看一下: Exception in thread "pool-1- ...

  2. java race condition_java多线程(一)Race Condition现象及产生的原因

    什么是Race Condition 首先,什么是Race Condition呢,Race Condition中文翻译是竞争条件,是指多个进程或者线程并发访问和操作同一数据且执行结果与访问发生的特定顺序 ...

  3. java race condition_Java中的Race condition和Critical section(译)

    Java中的Race condition和Critical section(译) race condition,即竞态,是一种可能发生于critical section中的特殊状态,critical ...

  4. java 线程 condition_Java编程中实现Condition控制线程通信

    java中控制线程通信的方法 1.传统的方式:利用synchronized关键字来保证同步,结合wait(),notify(),notifyall()控制线程通信.不灵活. 2.利用condition ...

  5. java的condition_java并发编程之Condition

    引言 在java中,对于任意一个java对象,它都拥有一组定义在java.lang.Object上监视器方法,包括wait(),wait(long timeout),notify(),notifyAl ...

  6. java学习(六)多线程 下

    线程之间是怎么通讯的呢? 线程之间的通信是多个线程操作同一个资源 代码: package tread; class Res{String name;String sex; } class input ...

  7. java 多线程缓存_[Java教程]【JAVA并发编程实战】12、使用condition实现多线程下的有界缓存先进先出队列...

    [Java教程][JAVA并发编程实战]12.使用condition实现多线程下的有界缓存先进先出队列 0 2016-11-29 17:00:10 package cn.study.concurren ...

  8. 2019-06-03 Java学习日记之多线程下GUI

    多线程下 单例设计模式: 保证类在内存中只有一个对象 如何保证类在内存中只有一个对象呢? 1.控制类的创建,不让其他类来创建本类的对象 2.在本类中定义一个本类的对象,Singleton s 3.提供 ...

  9. java condition_(原创)Java的ReentrantLock(可重入锁)下的Condition

    先来看一下这个Condition的使用场景,在LinkedBlockingQueue(链表的阻塞队列)类中包含如下的定义,通过使用lock.newCondition()方法,可以获得一个Conditi ...

最新文章

  1. dom4j的xpath查找xml的指定节点
  2. python绘制3d图形-Python基于matplotlib实现绘制三维图形功能示例
  3. java使用sigar监控服务器
  4. mysql 主从 问题_Mysql解决主从不同步问题
  5. 安安猜价格聪明机器人_5 项降噪优化,石头扫地机器人 T6 安静也有大吸力
  6. rest接口_深度干货 | 测试REST服务接口
  7. 回车 触发 提交事件
  8. JVM学习--(二)内存模型、可见性、指令重排序
  9. 小D课堂 - 新版本微服务springcloud+Docker教程_6-06 zuul微服务网关集群搭建
  10. vscode设置tab为4个空格_动图炫技23个鲜为人知的VS Code快捷键
  11. 最简单的OSPF配置实验(Cisco路由器)
  12. C语言处理按键的 单击(短按),长按,双击,多击 处理
  13. 软件设计模式Java版
  14. oracle手动删除dbf文件,oracle删除DBF文件
  15. 【论文笔记】Deep Survival: A Deep Cox Proportional Hazards Network
  16. 新安装Win10操作系统有必要设置的几个技巧
  17. message_filters学习笔记
  18. [云上贵州2017]智慧交通预测挑战赛
  19. 设计模式之禅之单一职责原则
  20. 二、ZFNet可视化卷积神经网络——可解释性机器学习(DataWhale组队学习)

热门文章

  1. 留数定理matlab,第五章 留数定理习题及其解答
  2. 药品搜索开发API接口-查询药品信息
  3. 最牛逼的Java编辑器,没有之一!
  4. Linux终端一直输出login,Linux tty pty console区别
  5. 小微贷款按小时算利息,会是互联网巨头“放贷梦“的终局吗?
  6. 现在手机千篇一律,下一个乔布斯的传奇在哪?
  7. Oxygen PDF Chemistry新功能
  8. 人工智能之深度学习常见应用方向你都了解吗?(文末包邮送书5本)
  9. 第2关:爬取表格中指定单元格的信息
  10. centos的wget无法解析域名