美文网首页
java并发编程要点

java并发编程要点

作者: Ronry | 来源:发表于2017-07-16 09:56 被阅读0次

    Java并发问题主要有三个核心概念:原子性,可见性,顺序性。

    原子性

    并发问题的原子性的概念和数据库事务的原子性是一样的:一个操作的多个步骤要么一起生效,要么一起回滚。
    java自带的并发控制的手段中,可以保障原子性的有:

    • synchronized 关键字
    • CAS
    可见性

    可见性的问题是指当变量被多个线程使用时,一个线程对其作出的修改操作,是否后续其他线程读的时候能马上读到修改后的值。可见性问题的产生是因为CPU为了加速数据读取读取的速度,采用了多级的告诉缓存。
    java中保障可见性的手段有

    • synchronized关键字
    • volidate关键字
    顺序性

    顺序指的是代码的执行顺序,顺序性指的是java编译器和CPU,在保障单个线程执行的时代码的逻辑结果不变的前提下,可能会对代码执行顺序的作出的一些改变(优化)。代码块在单线程执行的情况下,不管顺序怎么变,jvm一定会保证其逻辑不变,但是多个线程的情况下如果发生了重排序,则局部的逻辑可能是没变,单是全局的逻辑已经有问题了,比如下面的代码块

    class BadlyOrdered {  
      boolean a = false;  
      boolean b = false;  
      void threadOne() {  
        a = true;  
        b = true;  
      }  
    
      boolean threadTwo() {  
        boolean r1 = b; // sees true  
        boolean r2 = a; // sees false  
        return r1 && !r2; // returns true  
      }  
    }  
    

    方法threadOne在单线程执行的情况下,不管顺序怎么变,其最终的结果都是a和b都赋值为true。但是如果是多个线程,线程1执行方法threadOne,线程2执行方法threadTwo,即使不考虑可见性的问题,因为threadOne的执行顺序被排序,所以b的值得先被赋值为true,然后线程2读到了b=true和a=false,最后返回的结果将是true。
    那么怎么解决多线程情况下,顺序性带来的不确定问题呢。一种方法是在编码时显示使用synchronized关键,做到同一时间只有1个线程能操作共享的数据。另外一个方法运用jvm自带的happens-before规则。

    happens-before

    happens-before规则的描述如下

    Happens-Before Relationship : Two actions can be ordered by a happens-before relationship.If one action happens before another, then the first is visible to and ordered before the second.

    可见,happens-before规则同时对可见性和顺序产生的了影响。happens-before这个名称感觉还是挺有歧义的,从"before"这个字眼上看,以为它定义的是顺序性,其实"before"描述的是可见性的"before"。而顺序性,并不能简单的从字面描述去理解。比如,java规范定义了多条满足的happens-before规则,有一条是:

    A write to a volatile field happens-before every subsequent read of that volatile.

    它的"before"的定义,不能简单理解为对一个volatile变量的写操作发生在对其读操作之前。一个线程写,一个线程读,jvm可不知道哪个会先执行到。这里的"before"是可见性的before,也就是,一个volatile变量的写操作以及其后面的操作,对另外一个线程的发生在这之后的volatile变量的读操作以及之后的操作可见。
    而这条规则,对顺序性的真正的影响是:

    • 禁止把volatile写之前的行为与它重排序
    • 禁止把volatile读之后的行为与它重排序

    对于上面的示例程序,只要把变脸b用volatile关键字修饰,那threadOne方法就不会发生重排序了。也就是说happens-before规则也规定哪些类型的操作之间,编译器和CPU不能对其进行重排序,编程的时候只要利用这些内置的规则,就能消除重排序在多线程环境下的问题。
    总结来说,顺序性是java为了单线程执行的时候做的优化,但是它会在多线程环境下带来问题,并且这些问题基本是不能在编码阶段做理论分析的。为了解决这个问题,java又自己内置了一些规则(happens-before规则)对重排序做了一些限制,多线程编程的时候,只要利用这些规则,确保我们关心的关键点不会发生重排序,也就保证了程序的正确性。

    Lock接口

    我们知道Lock是jdk提供的并发控制工具包,使用Lock也能保证java并发的三个重要特性的。但是需要说的Lock只是一个工具包,它提供的保障都是依赖jvm提供的更底层的机制来保障的

    • 可见性: 是对实现中的锁变量使用volidate关键字,然后依赖volidate带来的happen-before特性,保障加锁和解锁之间的修改对其他线程可见的
    • 原子性:Lock本身的加锁解锁这两个行为的原子性是通过CAS操作保障的,而加锁之后的行为的原子性,是通过控制同一时间只能由1个线程执行,逻辑保障的
    • 顺序性: 同原子性,也是通过控制同一时间只能由1个线程执行的保障的
    总结
    原子性 可见性 顺序性
    sychronized 保证 保证 保证
    volatile 不保证 保证 部分保证
    CAS操作 保证 不保证 不保证
    Lock工具类 保证 保证 保证

    ####### refer
    http://www.infoq.com/cn/articles/java-memory-model-2
    http://ifeve.com/easy-happens-before/
    http://blog.csdn.net/admiral_dota/article/details/50489882

    相关文章

      网友评论

          本文标题:java并发编程要点

          本文链接:https://www.haomeiwen.com/subject/kqjahxtx.html