0
点赞
收藏
分享

微信扫一扫

Java内存模型及线程安全高性能编程


Java内存模型 vs  JVM运行时数据区

为什么java 能在不同的平台上跑?因为字节码是在虚拟机上跑,所以平台无关。遵循《Java虚拟机规范》 

Java内存模型及线程安全高性能编程_java

初看Java内存模型

来看看大家都是怎么定义的 :

Java虚拟机​可以同时支持多个执行线程,若未正确同步,线程的行为可能会出现混淆和违反直觉。

多线程程序的语义​,它包含了,​当多个线程修改了共享内存中的值时,应该读取到哪个值的规​则。由于这部分规范类似于不同硬件体系结构的内存模型,因此这些语义称为Java编程语言内存模型。

这些语义​没有规定如何​执行多线程程序。相反,它们描述了允许多线程程序的合法行为。

多线程中的问题

  1. 所见非所得
  2. 无法肉眼去检测程序的准确性
  3. 不同的运行平台有不同的表现
  4. 错误很难重现

有两个线程t1 和t2 ,一个标记, 当t1更改标记后,t2 没有读到更改后的标记。加上volatile关键字就收到了,示例代码如下。

public class Test1 {
private volatile boolean flag = true ;
public static void main(String[] args) throws InterruptedException {
Test1 demo1 = new Test1();
System.out.println("代码开始了");
new Thread(new Runnable() {
@Override
public void run() {
int i = 0 ;
while (demo1.flag){
i ++;
}
System.out.println("run i = "+ i);
}
}).start();
TimeUnit.SECONDS.sleep(2);
demo1.flag = false ;
System.out.println("被设置为false了");

}
}

//客户端输出
代码开始了
被设置为false了
run i = 201004010
复制代码

flag 的值 ,没有读到 ,可能原因是主线程没写入,或者副线程没读到。 flag 放在CPU的高速缓存中,时间片上有一瞬间值不一致,很快,所以不是高速缓存的锅  ,那是谁的呢,CPU指令重排。

Java内存模型及线程安全高性能编程_java_02

CPU指令重排序

Java编程语言的语义允许​Java编译器​和​微处理器​进行执行优化,这些优化导致了与其交互的代码不再同步,从而导致看似矛盾的行为。

单线程的指令重排-没有问题

循环的效率大于缓存,cpu想出来用赋值进行指令重排,A=0, B=0 。

Java内存模型及线程安全高性能编程_java_03

多线程的指令重排-有问题

0012 变成了 1122 。

Java内存模型及线程安全高性能编程_经验分享_04

Java内存模型及线程安全高性能编程_后端_05

JIT编译器(Just In Time Compiler)

脚本语言与编译语言的区别 ?

解释执行​:即咱们说的脚本,在执行时,由语言的解释器将其​一条条翻译​成机器可识别的指令 。翻译的价格有200块,500块不等。

编译执行​:将我们编写的程序,直接编译成机器可以识别的指令码。

_Java是脚本语言还是编译语言?Java_介于​脚本语言​与​编译语言​之间 

Java内存模型及线程安全高性能编程_开发语言_06

volatile 关键字

可见性问题:让一个线程对共享变量的修改,能够及时的被其他线程看到。

Java内存模型规定

对volatile变量v的写入,与所有其他线程后续对v的读同步

要满足这些条件,所以volatile关键字就有这些功能:

禁止缓存:

  1. volatile变量的访问控制符会加个ACC_VOLATILE 从链接得知 ACC_VOLATILE 0x0040 Declared volatile; cannot be cached.

       ​​docs.oracle.com/javase/spec…​​

  1. 对volatile变量相关的指令不做重排序

Shared Variables 定义

可以在线程之间共享的内存成为共​享内存或堆内存​。

所有​实例字段​、​静态字段​和​数组元素​都存储在堆内存中,这些字段和数组都是标题中提到的​共享变量​。

冲突​:如果​至少有一个访问是写操作​,那么对同一个变量的两次访问是冲突的

这些能被多个线程访问的​共享变量​是内存模型规范的对象。

线程间操作的定义

  1. 线程间操作指:一个程序执行的操作可被其他线程​感知​或被其他线程​直接影响​。
  2. Java内存模型只描述​线程间操作​,不描述线程内操作,线程内操作按照线程内语义执行。

Java内存模型及线程安全高性能编程_Java程序员_07

所有线程间操作,都存在可见性问题,JMM需要对其进行规范

对于同步的规则定义

  • 对​volatile​变量v的写入,与所有其他线程后续对v的读​同步
  • 对于监视器m的​解锁​与所有后续操作对于m的​加锁​同步
  • 对于每个属性写入​默认值​(0,false,null)与每个线程对其进行的操作同步
  • 启动线程​的操作与线程中的第一个操作同步
  • 线程T2的​最后操作​与线程T1发现线程T2已经结束同步。(isAlive,join可以判断线程是否终结)

Happens-before先行发生原则

Java内存模型及线程安全高性能编程_开发语言_08

Java内存模型及线程安全高性能编程_后端_09

final 在JMM 中的处理

Java内存模型及线程安全高性能编程_java_10

Word Tearing 字节处理

Java内存模型及线程安全高性能编程_Java程序员_11

double和long的特殊处理

Java内存模型及线程安全高性能编程_java_12

再看Java内存模型

Java内存模型及线程安全高性能编程_开发语言_13



举报

相关推荐

0 条评论