Java线程专题 3:java内存模型
1:内存模型基础
线程通信:共享内存和消息传递;在共享内存并发模型中,线程间共享程序的状态,通过写-读内存中的公共状态进行隐式通信;
在消息传递并发模型中,线程之间没有公共状态,线程之间通过消息传递进行显式通信
java的并发采用的是共享内存模型,Java线程之间的通信总是隐式执行的。
2:内存模型抽象结构
Java线程之间的通信由Java内存模型(JMM)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见。线程之间的共享
变量储存在主内存中,每个线程都有一个私有的本地内存,本地内存储存了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象
概念。
线程A和线程B要进行通信需要经历:
·1):线程A把本地变量更新过的共享变量刷新到主存中
2):线程B到主存中去读取线程A之前更新过的共享变量
3:重排序
执行程序时,为了提高性能,编译器和处理器有时会对指令作重排序。分为编译器重排序,指令重排序,内存系统重排序。
数据依赖:如果两个操作访问同一个变量,且这两个操作中有一个为写操作,则这两个之间就存在数据依赖
编译器和处理器不会对存在数据依赖关系的操作做重排序,因为这种重排序会改变执行结果
但是,如果操作之间不存在数据依赖关系,这些操作就可能被重排序。
4:volatile内存意义
可见性:对一个volatile变量的读,总能看到对这个变量最后的写入;
原子性:对任意单个volatile变量的读/写具有原子性,但类似volatile++这种不具有原子性
class VolatileDemo{ private int a = 0; private volatile boolean flag = false; public void writer(){ a = 1;//1 flag = true;//2 } public void reader(){ if(flag){//3 int i = a;//4 } } }
线程A执行writer方法后,线程B执行reader方法,根据happens-before关系后
1):次序规则 1->2, 3->4;
2) :volatile规则 2->3;
3): happens-before传递性 1->4;
最终 happens-before规则和volatile规则组合后保障多线程变量的正确性;
volatile实现是通过添加内存屏障
5:happens-before
double pi = 3.14; //A double r = 1.0; //B double area = pi * r * r; //C
上面计算圆面积中 AB 顺序改变不影响最终执行结果
A happens-before B
B happens-before C
A happens-before C
JSR-133对happens-before关系定义中明确
1): 如果一个操作happens-before 另一个操作,则第一个操作的执行结果对第二个的执行结果可见,并且第一个的执行顺序排在第二个之前
2):两个操作存在happens-before,并不表示java 平台具体实现需要安装该顺序,如果重排序后执行结果与按规则一致,也是可行的。
总结:
Java内存模型主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。此处的变量包括实例字段,静态字段和构成数组对象的元素。
1:java内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存。
2:关于主内存与工作内存之间的具体交互协议,即一个变量如何从主内存拷贝到工作内存,如何从工作内存同步回主内存子类的细节实现
java内存模型定义了八种操作:
lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占的状态。
unclock(解锁):作用于主内存的变量,把一个处于锁定的状态释放出来。
read(读取):作用于主内存的变量,把一个变量的值从主内存传输到线程的工作内存中
load(载入):作用于工作内存的变量,把read操作从主内存 得到的变量值放入工作内存的变量副本中。
use(使用):作用于工作内存的变量,把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的 值的字节码指令时将会执行这个操作。
assign(赋值):作用于工作内存的变量,把一个从执行引擎接收到的值 赋值给工作内存的变量,每当虚拟机遇到一个给变 量赋值的字节码指令时执行这个操作。
store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传递到主内存,以便write操作使用。
write(写入):作用于主内存的变量,把store操作从工作内存中得到的变量的值放入主内存的变量中。