Java内存模型(并发)

一.Java内存模型的基础

并发编程模型的两个关键问题:
  1. 线程之间如何通信:线程之间以何种机制交换信息;
  2. 线程之间如何同步:控制不同线程间操作发生相对顺序的机制;

线程之间的通信机制:

  • 共享内存:线程之间共享程序的公共状态,通过写读内存中的公共状态进行隐式通信(显式同步);
  • 消息传递:线程之间没有公共状态,线程之间通过发送消息显式进行通信(隐式同步);
Java内存模型的抽象结构:

       JMM(java内存模型)决定一个线程对共享变量的写入何时对另一个线程可见;线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存,本地内存存储了该线程以读/写共享变量的副本。

从源代码到指令序列的重排序:
  • 编译器优化的重排序:
  • 指令级并行的重排序:不存在数据依赖性;
  • 内存系统的重排序:处理器使用缓存和读写缓冲区,加载和存储操作可能是在乱序执行;
happens-before规则:

     如果一个操作执行的结果需要对另一个操作可见,这两个操作之间存在happens-before关系;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;
二.重排序

重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段;

数据依赖性:两个操作访问同一个变量,且这两个操作中有一个为写操作,这两个操作之间就存数据依赖性;

as-if-serial语义:不管怎么重排序(编译器和处理器为了提高并行度),(单线程)的执行结果不能被改变;

三.顺序一致性(理论参考模型)

内存模型:

  1. 一个线程中的所有操作必须按照程序的顺序来执行;
  2. (不管程序是否同步)所有线程都只能看到一个单一的操作执行顺序。每个操作必须原子执行且立刻对所有线程可见;

        同步程序的执行特性:顺序一致性模型中,所有操作完全按程序的顺序串行执行;在JMM中,临界区内的代码可以重排序(但JMM不允许临界区的代码逸出到临界区之外)

   未同步程序的执行特性:JMM只提供最小安全性,保证线程读取到的值不会无中生有,整体上是无序的,执行结果无法预知;

  1. 顺序一致性模型保证单线程内的操作会按程序的顺序执行,而JMM不保证所有单线程内的操作会按顺序执行;
  2. 顺序一致性模型保证所有线程只能看到一致的操作顺序,而JMM不保证所有线程能看到一致的操作执行顺序。
  3. JMM不保证对64位的long型和double型变量的写操作具有原子性,顺序一致性保证对所有的内存读/写操作具有原子性;
四.volatile内存语义

volatile特性:

  1. 可见性:对一个volatile变量的读,总是能看到任意线程对该变量的最后写入;
  2. 原子性:对任意单个变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性;

volatile写-读的内存语义:

  • 当写一个volatile变量时,JMM会把该线程对应的本地内存的共享变量值刷新到主内存;
  • 当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效,线程接下来从主内存中读取共享变量;

volatile内存语义的实现:

  1. 当第二个操作为volatile写时,不管第一个操作是什么,都不能重排序;这个规则确保volatile写之前的操作不会被编译器重排序到volatile写之后;
  2. 当第一个操作是volatile读时,不管第二个操作是什么,都不能重排序。这个规则确保volatile读之后的操作都不会被编译器重排序到volatile读之前;
  3. 当第一个操作是volatile写,第二个操作是volatile读时,不能重排序;

保守策略的JMM内存屏障插入策略:

  • 在每个volatile操作的前面插入一个StoreStore屏障;
  • 在每个volatile操作的后面插入一个StoreLoad屏障;
  • 在每个volatile操作的后面插入一个LoadLoad屏障;
  • 在每个volatile操作的后面插入一个LoadStore屏障;
五.锁的内存语义
  • 线程A释放一个锁,实质上是线程A向接下来将要获取这个锁的某个线程发出了(线程A对共享变量所做修改的)消息;
  • 线程B获取一个锁,实质上是线程B接收了之前某个线程发出的(在释放这个锁之前对共享变量所做修改的 )消息;
  • 线程A释放锁,随后线程B获取锁,即时线程A通过主内存向线程B发送消息;
六.final域的内存语义

final域的重排序规则:

  1. 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序;
  2. 初次读一个包含final域的引用,与随后初次读这个final域,这两个操作之间不能重排序;
七.双重检查锁定与延迟初始化

作用:推迟一些高开销的对象初始化操作,只有在使用这些对象时才进行初始化;

解决方案:

  1. 基于volatile的解决方案;
  2. 基于类初始化的解决方案;


你可能感兴趣的:(Java并发编程基础,memory)