首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java学习内存模型以及线程安全的可见性问题(八)

Java学习内存模型以及线程安全的可见性问题(八)

作者头像
IT架构圈
发布2020-07-02 22:27:22
4280
发布2020-07-02 22:27:22
举报
文章被收录于专栏:IT架构圈IT架构圈

上次线程池已经说过了,从今天开始一起了解下JVM内存模型详解。

(一)容易误解的部分

老铁很容易把JAVA的内存区域、JAVA的内存模型,GC分代回收的老年代和新生代也容易搞混,绕进去绕不出来。学习多线程之前一定要搞明白这些问题,可能在你的内心一直认为多线程就是一个工具,所有的底层都是C++来写的,没办法去看,为什么要有java,java其实就是屏蔽了底层的复杂性。

  • ① GC内存区域

堆的概念,老年代,新生代,Eden,S0,S1

  • ② JAVA的内存区域

JVM运行时的区域:java编译生成class,线程共享部分(方法区,堆内存),线程独占部分(虚拟机栈,本地方法栈,程序计数器)

  • ③ JAVA的内存模型(概念)

针对多核多CPU,多线程而制定的一套规范规则,不是一种开发技术。

(二)多线程中的问题
  1. 所见非所得(你看到的并不是所想的)、
  2. 无法肉眼去检测程序的准确性(多线程下,完全看不出来正常不正常)。
  3. 不同的运行平台有不同的表现。
  4. 错误很难重现。
(三)工作内存和主内存
  • ① 主内存

创建一个对象在堆里面,也可以称之为主内存,不仅仅是在堆,存在一个对象X,就存在主内存

  • ② 工作内存

线程运行在工作内存, 虚拟机栈,程序计数器,CPU,高速缓存。 工作内存和主内存只是一个逻辑上的划分,概念上的东西。

  • ③ 奇妙的现象

主内存的flag传输到工作内存flag的时候,存在CPU缓存的情况,CPU缓存可能导致非常短的时间内不一致,本身CPU厂家底层是要做一致处理的,但是存在短时间内的不一致。

(四)指令重排
  • ① 介绍

Java语言规范JVM线程内部维持顺序或语义,即只要程序的最终结果与它顺序化情况的结果相等,那么指令的执行顺序可以与代码逻辑顺序不一致,这个过程就叫做指令的重排序。

  • ② 意义

使指令更加符合CPU的执行特性,最大限度的发挥机器的性能,提高程序的执行效率。 重排序,只能保证单个线程的,如果是多线程的话,就没有爆发保证重排序。

java // 线程1 a = d; b = 2 // 线程2 c = a; d =3 //重排序后 //线程1 b = 2 ; a =d; //线程2 d = 3 ; c =a; ¨G0G java public class VisibilityDemo2 { // 状态标识 (不用缓存) private volatile boolean flag = true; ¨K38K } ¨G1G java import java.util.concurrent.TimeUnit; public class VisibilityDemo1 { // 状态标识 private static boolean is = true; ¨K39K } ¨G2G java public class VisibilityDemo { private volatile boolean flag = true; ¨K40K }

  • ③ 启动线程的操作与线程中的第一个操作同步
  • ④ 对于每个属性写入默认值(0,false,null)与 每个线程对其操作的同步
  • ⑤ 线程T1的最后操作与线程T2发现线程T1已经结束同步(isAlive,join可以判断线程是否终结)
  • ⑥ 如果线程T1终端了T2,那么线程T1的中断操作与其他所有线程发现T2倍中断了同步,通过抛出InterruptedException异常,或者调用Thread.interrupted 或者 Thread.isInterrupted。
(八)Happyens-before先行发生原则
  • ① 介绍

强调两个有冲突的动作之间的顺序,以及定义数据征用的发生时机。

  • ② 原则
  1. 同一个线程里面对数据做了变动,后面的动作可以及时的看到,其实还是可见性。
  2. 某个monitor上的unlock动作 happens-before 同一个monitor上后续的lock动作。
  3. 对某个volatile 字段的写操作 happens-before 每个后续对该 volatile 字段的读操作。
  4. 在某个线程对象上调用start() 方法 happens-before 该启动了的线程中的任意动作。
  5. 某个线程中的所有动作 happens-before 任意其他线程成功从该线程对象上的join() 中返回。
  6. 如果某个动作 a 在happens-before 动作 b,b 在happens-before 动作 c,则 a happens-before c。
(九) final 在JMM中的处理
  • ① final在该对象的构造函数中设置对象的字段,当线程看到该对象时,将始终看到该对象的final字段的正确构造版本。
  • ② 如果在构造函数中设置字段后发生读取,则会看到该final字段分配的值,否则它将看到默认值。
  • ③ 读取该共享对象的final成员变量之前,先要读取共享对象。
  • ④ 通常static final 是不可以修改的字段。然而System.in, System.out 和 System.err 是static final 字段,遗留原因,必须允许通过set方法改变,这些字段称为写保护,以区别于普通的final字段。

PS:使用了volatile,unlock和lock的时候,就可以保证代码不进行重排序。内存模型java进阶的一个核心点,这个理解了,其实比写多少年的业务代码要重要很多。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-07-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程坑太多 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • (一)容易误解的部分
  • (二)多线程中的问题
  • (三)工作内存和主内存
  • (四)指令重排
  • (八)Happyens-before先行发生原则
  • (九) final 在JMM中的处理
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档