深入理解java内存模型一

并发编程模型的分类

概要

并发编程中,需要处理两个问题:

  1. 线程之间如何通信
  2. 线程之间如何同步

1.线程通信:

定义:是指线程之间以何种机制来交换信息。
在命令式编程中,线程通信机制有两种:共享内存消息传递

共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式的通信。
消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送消息来显示的进行通信。

2.同步:

定义:是指程序用于控制不同线程之间操作发生相对顺序的机制。
共享内存并发模型里,同步是显示进行的。程序必须指定某个方法或某个代码段需要在线程之间互斥执行。
消息传递并发模型里,由于消息的发送必须在消息的接收之前,因此同步是隐式进行的。

3.java内存模型特性

java采用的是共享内存模型,java线程之间的通信是隐式进行,整个通信过程对程序透明。

java内存模型的抽象

在java中,所有实例域,静态域和数组元素都存储在堆内存中,堆内存在线程之间共享(共享变量)
局部变量(Local variables),方法定义参数(java语言规范称之为formal method parameters)和异常处理器参数(exception handler parameters)不会在线程之间共享,它们不会有内存可见性问题,也不受内存模型的影响。

Java线程之间的通信由Java内存模型(本文简称为JMM)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见。

从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。

本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

重排序

为了提高性能,编译器和处理器常常会对指令做重排序。重排序分为三种类型:

  1. 编译器优化的重排序。
  2. 指令级并行的重排序
  3. 内存系统的重排序。

处理器重排序与内存屏障指令

处理器对内存的读/写操作的执行顺序,不一定与内存实际发生的读/写操作顺序一致!

这里处理器A和处理器B可以同时把共享变量写入自己的写缓冲区(A1,B1),然后从内存中读取另一个共享变量(A2,B2),最后才把自己写缓存区中保存的脏数据刷新到内存中(A3,B3)。当以这种时序执行时,程序就可以得到x = y = 0的结果。
从内存操作实际发生的顺序来看,直到处理器A执行A3来刷新自己的写缓存区,写操作A1才算真正执行了。虽然处理器A执行内存操作的顺序为:A1->A2,但内存操作实际发生的顺序却是:A2->A1。此时,处理器A的内存操作顺序被重排序了。

happens-before

如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系。这里提到的两个操作既可以是在一个线程之内,也可以是在不同线程之间。

与程序员密切相关的happens-before规则如下:

  • 程序顺序规则:一个线程中的每个操作,happens- before 于该线程中的任意后续操作。
  • 监视器锁规则:对一个监视器锁的解锁,happens- before 于随后对这个监视器锁的加锁。
  • volatile变量规则:对一个volatile域的写,happens- before 于任意后续对这个volatile域的读。
  • 传递性:如果A happens- before B,且B happens- before C,那么A happens- before C。




posted @ 2018-06-03 15:16  hptjzzj  阅读(301)  评论(0编辑  收藏  举报