Java多线程的一些挑战

在学习操作系统的过程中,已经对多线程会带来的一些问题有了一定的认识,但是这些仍然有所不足,这里补充记录一下。

原子性问题

原子性的核心含义是,一个操作不可分割,要么已经完全执行成功,要么就是完全没有被执行。只能处于这两种状态之一。原子性在多线程编程中,是确保不会出错的最基本要求。但有一个错误的理解,就是java当中,多线程安全的类型,即便在上执行非原子性的操作,也能够保证得到正确的结果。这个理解是错误的。

举例说明,concurrentHashMap是一个线程安全类型,其中的value被标记为volatile。但是如果在更新kv对的value时,采取的是读出value, 更新value,写入value这种操作,则无法保证一定会有正确的结果。原因在于,读取更新写入当中,操作线程可能会被切出,假设更新value已经完成,操作线程正准备进行写入的时候线程被切出,如果此时有另外一个线程也对value进行更新操作,并且进行了写入。那么很显然,再切回去的时候,由于是基于读取的value进行更新的,写入的也是一个错误的数据。这并没有违背concurrentHashMap是线程安全的这个定义,因为concurrentHashMap通过对指定segment的加锁保证了只有一个线程在写入,这里并没有两个线程一起写入,只有一个线程拿到了锁。但是由于操作逻辑性的问题,写入加了锁,没有竞态,还是被写入了错误的值。

可见性问题

HashMap是一个非线程安全类,对比多线程安全类concurrentHashMap,一个区别是,HashMap中的value,并没有使用volatile修饰。这可能带来的一个问题就是,某个线程对HashMap当中的value进行更新,另外一个线程可能永远读取不到这个最新的value值。可见性问题比较隐蔽,可能由于不同的架构,在不同的平台带来不一样的结果。使用volatile进行修饰之后,能够保证变量更新,另外一个线程进行读取,就一定会得到最新的值。这是通过每次读取写入都是直接向内存进行而不是寄存器,达到的效果。一个需要注意的点是,寄存器读取可能只需要若干个机器周期,但是读取内存的耗时通常在70-80,甚至100ns这个量级,因此,读取一次内存,如果不做任何优化的话,CPU需要干等三百个机器周期上下。这很浪费效率。

有序性问题

为了优化执行效果,许多编译器都会对代码的执行顺序进行重新排序优化,来达到最大化利用CPU指令流水线的目的。在java程序中,这个过程一般是在jvm上进行的,也就是jit动态编译器来进行的指令重排序。指令重排序有时候会带来奇葩的结果,比如,由于指令重排序,引用已经赋给了地址,但是地址内并没有进行对象的初始化,以至于对象内部的某些值仍然保持0的状态。在concurrentHashMap当中,读取键值对的操作如下

V get(Object key, int hash) { 
           if(count != 0) {       // 首先读 count 变量
               HashEntry<K,V> e = getFirst(hash); 
               while(e != null) { 
                   if(e.hash == hash && key.equals(e.key)) { 
                       V v = e.value; 
                       if(v != null)            
                           return v; 
                       // 如果读到 value 域为 null,说明发生了重排序,加锁后重新读取
                       return readValueUnderLock(e); 
                   } 
                   e = e.next; 
               } 
           } 
           return null; 
       }

读操作一开始并不进行加锁操作,而是直接尝试读取其中的内容,但是键值对Entry并非空的时候,读取出来的value却可能为null。由于concurrentHashMap并不支持保存的键值对为空的情况,因此出现这种情况的合理解释,就是value指向的内存区域,由于指令重排序,暂时没有赋值完成,此时则需要加锁之后重新读取,则可读取出正确的值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值