当前位置: 移动技术网 > IT编程>开发语言>Java > java并发 - 自底向上的原理分析

java并发 - 自底向上的原理分析

2018年03月04日  | 移动技术网IT编程  | 我要评论

   

  事先声明,我只是java并发的新手,这篇文章也只是我阅读《java并发编程的艺术》一书(内容主要涉及前3章)的一些总结和感悟。希望大家能多多讨论,对于错误的地方还请指出。

  0. 简介

    程序的世界是有层次分明的,每层都对外封装细节而提供一些方式或者说接口来提供功能,甚至是约束功能来换取正确性等等。那么接下来,就用分层的思想作为灵魂,各种内存模型作为骨架,来简单讨论Java并发的原理。
 

  1. 处理器内存模型

    我们面对的第一个问题是,一个好的处理器要满足什么条件?就像考试的学生一样,当然是做得又快,对得又多。对于处理器来说一般要满足两个条件:
    1. 效率
    2. 正确性
 

  1.1 效率

    对于效率来说,处理器提供了很多的机制来满足这一点,并行,指令流水线,指令重排,缓存机制等等。但这些方式必须加以限制来保证正确性,或者说效率和正确性在一定程度上是相互制约的,我们必须加以权衡,合理折中。
 

  1.2 正确性

    那么怎么保证正确性呢?同样除了处理器的一些内部的机制,比如禁止某些指令重排,缓存一致性,总线锁,缓存锁定等等,同时还向其上层提供了好的方法,比如说内存屏障,CAS等等,这些方法、机制会让上层(通过合理的方式)更好的来保证程序的正确性,就像Java会提供volatile,synchronized等等方式来让程序员(通过合理的方式)调用保证程序的线程安全。
    让我们更加深入这个话题,一个程序怎么样才叫正确?要满足什么性质?简单来说,有三条性质:
    1. 原子性,就是要么全部做要么全不做,需要硬件的配合,比如CAS。
    2. 可见性,由于缓存的存在,要让一个处理器处理的结果对其他处理器可见,首先要把缓存写入内存。
    3. 顺序性,程序必须保持一定的顺序依次的产生结果。

    注意,处理器的很多机制可以保持其中多种性质,比如内存屏障就可以把缓存刷新进入内存,而其他处理器的缓存无效来保证可见性,和通过禁止指令重排来保证顺序性。比如总线锁,缓存锁定,缓存一致性就通过各种方式来保证顺序性和原子性。
 

  2. java内存模型

    好了我们更上一层楼,我们刚刚说在下一层(处理器)提供了各种机制来保证效率和正确性,而这一层正是要使用这些机制来到达这一层,也就是JRE和编译器的正确性和效率。
    JMM(即java内存模型)同处理器模型一样,也有很多的机制,包括各种优化。对外也提供volatile,synchronized和臭名昭著的concurrent包。上层的程序员也要使用(合理的方式)这些Java语言和包来保证线程安全。以下简单叙述一些本层次与上一层次的依赖(注意,synchronized因为JMM的优化可以分为3种)

    1. final域 -> 内存屏障

    2. volatile -> 内存屏障(字节码层次)-> 内存屏障(处理器层次)

    3. synchronized -> 偏向锁 + 轻量级锁 + 互斥锁
    偏向锁 -> CAS
    轻量级锁 -> CAS
    互斥锁 -> moniter -> 原子级处理器指令

    4. concurrent包 -> volatile + CAS

    注意CAS实现concurrent包中的原子操作可能会有三大问题:
    1. ABA问题。(解决办法:在变量前增加版本号)
    2. 循环时间长开销大。(解决办法:pause指令)
    3. 只能保证一个共享变量的原子操作。(解决办法:用锁或着合并操作)

    JMM把各种控制程序顺序性的机制抽象成文字形式,就是happens-before。其目的就是向Java开发者屏蔽特定平台的底层细节,设计的程序只要遵守happens-before就可以保证正确性。
 

  3. 程序员

    这是最上面的层次,简单来说就是来利用好下层提供的机制来更好的编程。我想这也是我们要学习java并发原理的原因。

 

 

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网