已经进入到第三章——java内存模型了,本篇先对这个概念做一个了解。
第三章 Java内存模型(一) Java内存模型的基础
并发编程的两个关键问题
线程之间如何通信
通信即交换信息的机制,线程之间的通信有两种方法:一是通过读/写内存中的共享变量进行隐式通信,一是线程之间互发消息进行显式通信,Java的并发采用的是共享内存模型,java线程之间的通信总是隐式的。
线程之间如何同步
同步是指程序中用于控制不同线程间操作发生相对顺序的机制,举个例子,一个线程要执行int i = 2,另一个线程要执行int j = i,那么第二个线程就不能运行在第一个线程之前,即需要保持相对顺序,否则就会报错。
Java内存模型的抽象结构
Java内存模型也简称为JMM(Java Memory Model),由于总是隐式通信,因此JMM就需要对内存中的共享变量进行管理:
- 局部变量,方法参数和异常处理器参数不是共享变量,不必考虑;
- JMM定义了线程和主内存之间的抽象关系:共享变量存储在主存中,同时每个线程也各有一个私有的本地内存,里面存储了共享变量的读写副本,但本地内存是个抽象概念,实际并不存在,它是一些区域的集合,比如缓存;
- JMM就是通过控制线程写入的数据何时对另一个线程可见来实现线程通信的,比如线程A和线程B通信,线程A就需要先将本地内存中的数据写入主存,之后线程B就可以去读取主存,以获取线程A的数据。
重排序
编译器和处理器常常会对指令做重排序,如果两条指令互不影响,就可以用两个线程同时进行操作,这样就提高了性能。Java源码编译到最终执行会依次经历三种重排序:
1. 编译器优化的重排序。编译器在不改变语义的情况下,会对程序进行重排序;
2. 指令级并行的重排序。如果不存在数据依赖性,处理器可以将多条指令重叠执行;
3. 内存系统的重排序。
重排序一般情况下能显著提高性能,但是必须要在不改变原来语义的基础上才能进行,针对源码中的各种约束或依赖,JMM的处理器重排序规则会要求Java编译器在生成指令序列时,插入特定类型的内存屏障(Memory Barriers,Intel称之为Memory Fence)指令,通过内存屏障指令来禁止特定类型的处理器重排序。
并发编程模型的分类(略)
happens-before
Java的JSR-133内存模型使用happens-before的概念阐述操作之间的内存可见性。
在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间就存在着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。
需要注意的是,happens-before关系,并不意味着一个操作必须要在后一个操作之前执行,而仅仅要求前一个操作的结果对后一个操作可见,具体原因后面会详细解释。