JAVA内存模型(学习记录)

来源:互联网 发布:什么软件制作数据图表 编辑:程序博客网 时间:2024/06/06 11:37

一、java内存模型

java线程之间的通信是通过共享内存模型实现,这里的共享内存模型就是指java内存模型(JMM)。JMM决定了一个线程的变量何时对另一个变量可见。线程之间的共享变量存储在主内存中,而在线程的本地内存会存有共享变量的副本。通常来说,当A,B2个线程之间需要共享变量时,A线程先将本地内存改变后的变量副本刷新到主内存中,B线程在主内存中读取更新后的共享变量。

java内存区域的划分主要分为方法区、虚拟机栈,本地方法栈,堆,程序计数器。

1、方法区:存储被加载的类的信息,常量,静态变量,以及即时编译器编译后的代码等数据。

2、虚拟机栈:主要存放线程执行的局部变量表,操作数栈,动态链接,方法出口等信息,是线程私有的。其实内部存储的即是栈帧,栈帧只有最上部的处于活跃状态,栈帧与栈帧之间有重合部分,用于数据交换。

3、本地方法栈:是为了native方法服务,线程私有的。

4、堆:堆区用于存放所有的对象实例,可以被多线程共享。为了GC回收机制的方便,根据对象存活周期的长短将堆分为新生代和老生代。具体GC回收机制还没学习到。

5、程序计数器:当前线程执行的字节码信号指示器。当字节码解释器工作时,通过这个读取计数器的值来获取下一个需要执行的字节码指令。

java内存模型与硬件结构并不保持一致。在硬件方面,不分栈与堆,大多数数据都保存在内存中,少部分在CPU寄存器中。当对象和变量储存在计算器的各个内存中时,就会出现以下的问题:

1、共享对象的可见性 

共享对象存储在主存中,当一个cpu的线程从缓存中将共享对象更改了,没有及时将更改后的对象flush到主存中,此时线程对共享对象的更改对其他线程就是不可见的。最终每个线程都会拷贝共享对象,而共享对象位于不同的cpu中。

解决这个问题需要使用volidate关键字,其会让线程对变量的读取直接来自主存。更新后会直接flush到主存,而不经过cpu缓存。

2、共享对象的竞争关系

当多个线程共享一个对象,它们同时修改这个共享对象时,就产生了竞争现象。

如果一个线程A从主存中获取共享obj到CPU缓存,线程B也读取共享obj到cpu缓存,如果两个线程都对obj中的变量进行加1操作,则obj的变量加1操作被执行了两次,只是缓存到了不同的cpu中。当两者是串行时,执行是正确的。但是当两者并行的时候,则不论是A还是B先执行flush将结果保存到主存中,最后的结果都是obj的变量增加了一次。

解决这个问题,就需要使用java的synchronized,ynchronized代码块可以保证同一个时刻只能有一个线程进入代码竞争区,synchronized代码块也能保证代码块中所有变量都将会从主存中读,当线程退出代码块时,对所有变量的更新将会flush到主存,不管这些变量是不是volatile类型的。