Java多线程学习之内存模型

目录

1. Java的内存模型

1.1 JVM内存结构

1.2 多线程下Java的内存模型

1.3 多线程的不可见问题


1. Java的内存模型

1.1 JVM内存结构

Java中的每个线程拥有独立的栈空间,但是同一个进程中的多线程共享一块堆内存,JVM的内存结构:

 

1)程序计数器:每个线程私有的,当前线程执行字节码的行号指示器

2)虚拟机栈:每个线程被分配一段栈,线程之间的栈也是互相隔离的。每个方法被执行的时候都会同时创建一个栈帧,栈它是用于支持虚拟机进行方法调用和方法执行的数据结构。栈帧用于存储局部变量表、操作数栈、动态连接、方法返回地址和一些额外的附加信息。在编译程序代码时,栈帧中需要多大的局部变量表、多深的操作数栈都已经完全确定了,并且写入了方法表的Code属性之中。因此,一个栈帧需要分配多少内存,在编译时已经确定,不会受到程序运行期变量数据的影响,而仅仅取决于具体的虚拟机实现。

  •  局部变量表:存放方法参数和方法内部定义的局部变量
  •  操作数栈:相当于寄存器,存放操作数(运算用到的数),但java虚拟机是基于栈的,将一个操作数入栈,稍后用到时将其出栈。
  •  动态连接:符号连接在运行过程中转为动态连接

3)本地方法栈: 该区域与虚拟机栈所发挥的作用非常相似,只是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为使用到的本地操作系统(Native)方法服务。

4)Java堆:所有线程共享的一块内存区域,所有的对象实例和数组都在这里分配。垃圾收集器管理的主要范围,又叫CG堆。

5)方法区:线程共享的内存区域;存储已经被虚拟机加载的类信息、常量、静态变量、即编译器编译后的代码。Java虚拟机规范把方法区描述为Java堆的一个逻辑部分(也在堆内存中?)。虚拟机允许该区域选择不实现垃圾回收。运行时常量池是方法区的一部分(Class文件中的常量池信息,用于存放编译器生成的各种字面量和符号引用,在类加载后存放到方法区的运行时常量池)

1.2 多线程下Java的内存模型

Java内存模型中包括主内存和每个线程独立的工作内存

主内存:所有共享变量

工作内存:当线程需要访问某个共享变量时,将主内存中变量的值拷贝一份到工作内存中,存放在缓存或CPU寄存器中

当一个线程需要对共享变量进行修改时,先从主内存复制变量到工作内存,操作之后再将新的变量值刷新到主内存中。因此当多个线程同时操作某个共享变量时,就有可能出现不可预测的结果。

1.3 多线程的不可见问题

Java的内存模型导致了多线程不可见的问题,所谓不可见就是:某个线程对共享变量进行了修改,但其他线程并不知道。

比如说A和B同时操作某个共享变量时,A将更新值存回主内存,而此时B还保存原来的副本值,该更新值对B不可见

解决不可见问题的措施:

  • volatile关键字:用volatile关键字标记的变量,具有以下效果:
    1. 禁止指令重排序(为了优化jvm有时对代码顺序进行重排序,但单线程情况下不会影响程序执行的结果)
    2. volatile变量不会被缓存在工作内存中,线程每次访问该变量都要从主内存中获取,因此总是返回最新值

需要注意的是volatile关键字并不能保证线程安全,因为它只能保证线程可见性,不能保证原子性

  • 同步措施,synchorized关键字、各种Lock、原子变量等

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值