美文网首页
java多线程(八)JMM和底层实现原理

java多线程(八)JMM和底层实现原理

作者: 7ColorLotus | 来源:发表于2020-05-27 23:11 被阅读0次
  • 线程之间的通信
    1. 线程的通信是指线程之间以何种机制来交换信息。在编程中,线程之间的通信机制有两种,共享内存和消息传递。
    2. 在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信,典型的共享内存通信方式就是通过共享对象进行通信
    3. 在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送消息来显示进行通信,在java中典型的消息传递方式就是wait()和Notify()
  • 线程之间的同步
    1. 同步是指程序用于控制不同线程之间操作发生相对顺序的机制
    2. 在共享内存并发模型里,同步是显示进行的。程序员必须显式指定某个方法或某段代码需要在线程之间互斥执行
    3. 在消息传递的并发模型里,由于消息的发送必须在消息的接收之前,因此同步是隐式进行的。
  • java内存中的共享
  • java内存中的重排序
    1. 编译器优化重排序
    2. 指令集并行重排序
    3. 内存系统重排序
  • 重排序与依赖性
    1. 数据依赖性
    2. 控制依赖性
    3. as-if-serial
  • 解决并发下的问题
    1. 内存屏障 :a,禁止重排序,b,强制刷出Cache
      四种屏障类型:
      1> LoadLoadBarriers :Load1.LoadLoad.Load2.Load2
      2> StoreStoreBarriers :Store1.StoreStore.Store2
      3> LoadStoreBarriers : Load1.StoreStore.Store2
      4> StoreLoadBarriers :Store1.StoreStore.Load2
    2. 临界区(锁)
  • Happens-before
    1. 用Happens-before的概念来阐述操作之间的内存可见性。在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系。
    2. 两个操作之间具有happens-before关系,并不意味着前一个操作必须要在后一个操作之前执行!happens-before仅仅要求前一个操作执行的结果对后一个操作可见,且前一个操作按顺序排在第二个操作之前
    3. Happens-before规则。无需任何同步手段就可以保证的:
      1> 程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作
      2> 监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁
      3> volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读
      4> 传递性:如果A happens-before B,且 B happens-before C,那么A happens-before C
      5> start()规则:如果线程A执行操作ThreadB.start(),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作
      6> join()规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作Happens-before于线程A从ThreadB.join()操作成功返回
      7> 线程中断规则:对线程interrupt()方法的调用happens-before于被中断线程的代码检测到中断操作的发生
  • volatile的内存语义:
    1. 可以把对volatile变量的单个读/写,看成是使用同一个锁对这个变量单个读/写操作做了同步
    2. volatile写的内存语义:当写一个volatile变量时,JMM会把该线程对应的本地内存中的所有共享变量值刷新到主内存
    3. 当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量
    4. JMM对volatile的内存屏障插入策略
      1> 在每个volatile写操作的前面插入一个StoreStore屏障,在每个volatile写操作的后面插入一个StoreLoad屏障
      2> 在每个volatile读操作的后面插入一个LoadLoad屏障,在每个读操作的后面插入一个LoadStore屏障
  • 锁的内存语义
    1. 当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中
    2. 当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视保护的临界区代码必须从主内存中读取共享变量
  • final的内存语义
    1. 重排序规则
      1> 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序
      2> 初次读一个包含final的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序
      3> final域为引用类型增加如下规则:在构造函数内对一个final引用的对象的成员域的写入,与随后在构造函数外把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序

    2. final语义在处理器中的实现
      1> 会要求编译器在final域的写之后,构造函数return之前插入一个StoreStore屏障
      2> 读final域的重排序要求编译器在读final域的操作前面插入一个LoadLoad屏障

  • volatile的实现原理
    1. 有volatile变量修饰的共享变量进行写操作的时候会使用CPU提供的LOCK前缀指令
    2. 将当前处理器缓存的数据写回到系统内存
    3. 这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效
  • Synchronized的实现原理
    1. 使用monitorenter和monitorexit指令实现的
      1> monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处
      2> 每个monitoryenter必须有对应的monitorexit与之配对
      3> 任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态
    2. 了解各种锁状态(级别从低到高)
      1> 无锁状态
      2> 偏向锁状态:大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得所的代价更低而引入了偏向锁。无竞争时不需要进行CAS操作来加锁和解锁
      3> 轻量级锁状态:通过CAS操作来加锁和解锁
      4> 重量级锁状态
  • 自旋锁:通过循环CAS操作拿到锁,是拿锁的一种方式

相关文章

网友评论

      本文标题:java多线程(八)JMM和底层实现原理

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