内存模型

Posted wheleetcode

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内存模型相关的知识,希望对你有一定的参考价值。

1 Java程序最初通过解释器进行解释执行,当虚拟机发现某个方法或代码块的执行特别频繁时,就把这些代码认定为热点代码,为了提高执行效率,在执行时虚拟机把这些代码编译成本地平台相关机器码,热点代码。

2 Java内存模型主要定义程序中各个变量的访问规则,即将虚拟机中将变量存储到内存和从内存取出变量这样的底层细节,变量主要包括实例字段,静态字段,和构成数组对象的元素,不包括局部变量和方法参数。

3 关于主内存与工作内存之间如何交互,Java内存模型定义了8种原子操作。

1lock 主内存变量,标识一个变量线程独占  2 unlock 主 把一个处于锁定状态变量释放, 3 read 主 把变量从主内存传到工作内存,以便load   4 load 工作内存,把read操作从主内存中得到的变量放入工作内存变量副本中, 5 use 工作内存 把工作内存变量传给执行引擎, 6 assign 工作内存

把执行引擎接收到的值赋给工作内存变量。7 store 工作 把工作内存变量传到主内存,以便write   8 write 主 把store操作得到的变量放入主内存。

4 volatile变量规则

两种特性 1 变量对所有线程的可见性,是通过使用变量时从主内存刷新得到。运算结果不依赖当前值,不需要同其他变量参与不变约束

 2 禁止指令重排序,在编译后的语言中有汇编代码lock  相当内存屏障,重排序时不能把后面指令重排到前面,使本CPU的cache写入内存,

 内存访问规则 1 使用变量T前必须连续use load  read ,以便使用变量前从主内存刷新值,看到其他线程对变量的修改

2 在对变量赋值后要assign  store write 一起,每次修改后必须同步到内存中,用于保证其他线程可以看到自己对变量的修改。

3 A是线程T对变量v的use或assign动作,F是和A关联的load或store动作,P是和F关联的read或write  类似的B是线程T对变量w的use或assign动作,G是和B关联的load或store动作,Q是和G关联的read或write,如果A先于B,那么P先于Q,保证volatile变量不会被重排序

5 内存模型是围绕并发过程中如何处理原子性,可见性,和有序性3个特征来建立的。

原子性:  6 个原子操作,monitorenter monitorexit 字节码反映代码synchronized同步块,

可见性:一个线程修改了共享变量的值,其他线程立即可见,volatile新值能立即同步到主内存,每次使用从主内存刷新,synchronized 同步块,变量执行unlock前,必须同步到主内存, final 在构造器初始化完成,其他线程立即见到final值。

有序性,本线程观察有序,其他线程无序, 线程内表现串行,后指令重排,工作内存和主内存同步延迟。一个变量在同一时刻只能一个线程

lock。

 6 先行发生原则, Java内存模型定义的两项操作之间的偏序关系,如果说操作a先行发生于操作b,也就是说在发生b前,操作a产生的影响能被b观察到。

    Java语言天生先序规则,1 程序次序规则,在一个线程内,写在前面操作先行于写在后面操作 2 锁管理,一个unlock先行于后面对同一个锁的lock, 3 volatile变量规则,对一个volatile变量写操作,先行与后面对这个变量读操作,4 线程启动 thread对象start方法先行于线程的每个动作 5 线程终止 线程所有操作先行发生于对线程终止检测 6线程中断 对线程interrupt()方法调用,先行发生于被中断线程代码检测到中断事件发生。7 对象终结 一个对象初始化完成先行发生于finalize方法开始 8 传递 a 先 b, b先c , a先 b

7 Java线程的实现

     1 使用内核线程   - 轻量级进程-   基于内核实现,线程创建,析构,同步需要进行系统调用,代价高,在用户态和内核态切换,消耗 内核资源,支持的线程有限

     2用户线程实现,完全建立在用户空间的线程库上,系统不能感知线程存在,不需切换到内核态。比较复制。

    3 用户线程加轻量级进程 用户线程完全建立在用户空间上,用户线程创建,切换,析构廉价,大规模,使用内核线程的调度功能及处理器映射,用户线程系统调用要通过轻量级进程完成。

8 线程调度

    指系统为线程分配处理器使用劝的过程,协作式和抢占式。

9 线程安全等级把共享数据分为5类

   1 不可变:不可变对象一定线程安全。

   2 绝对线程安全:不管运行环境如何,调用者都不需要任何额外的同步措施,调用对象的行为都能获得正确结果。

 3 相对线程安全:对对象的单次操作是线程安全的,在调用的时候不需要额外的措施。 vertor

 4 线程兼容 对象本身不是线程安全,但可以在调用端正确使用同步手段保证线程安全。 arraylist

5 线程对立 无论在调用端是否采取同步无法在多线程环境并发使用代码。

实现同步方法

1 synchronized 经编译后,会在同步块前后形成monitorenter和monitorexit字节码指令,

ReentrantLock特性 1 等待可中断:持有锁的线程不放锁时,等待线程可以放弃等待,处理其他事情,对处理非常长的同步块有帮助

2 公平所:3 绑定多个条件:一个ReentrantLock对象可以同时绑定多个Condition对象,一个锁可以和多个条件关联。

阻塞同步:悲观并发策略,认为只有不去做正确的同步措施,肯定会出问题,无论共享数据是否会出现竞争,都要加锁

冲突检测:乐观:先进行操作,如果没有线程争共享数据,操作成功,如果共享数据有争用,产生了冲突,采用其他补偿措施。

锁优化:

自旋锁:往往线程持有锁的时间不是很长,可以让后面请求锁的线程等待一下,但不放弃处理器执行时间

自适应自旋: 自旋时间不固定,由前一个在同一个锁上的自旋时间及锁拥有状态决定,如果在同一个锁上,自旋刚刚成功,虚拟机认为本次自旋也可能再次成功,进而允许自旋更长时间,

锁消除:虚拟机即时编译器,对一些代码上要求同步,但检测到不可能出现数据共享,进行锁消除。

锁粗化:如果一系列的连续操作对同一个对象反复加锁和解锁,消耗性能,把锁同步范围扩展到操作序列外部

轻量级锁:无竞争的时候消除互斥量

加锁:在代码进入同步块的时候,如果代码没有被锁定(锁标志01),虚拟机在当前线程栈帧建锁记录(lock record)空间,用于存储对象mark word 拷贝,然后虚拟机将用cas操作尝试将对象头mark word更新为指向lock record指针,如果更新成功,mark word标志位00,轻量级锁。否则说明锁对象已经被其他线程抢占,状态10,膨胀为重量级锁,后面等待线程阻塞。

解锁:通过cas把对象当前mark word和线程中复制的替换过来,如果替换完成,同步完成,替换失败,有其他线程尝试获取锁,释放锁的时候。唤醒挂起线程。

偏向锁:无竞争下消除cas  偏向于第一个获取他的线程,如果锁没有被其他线程获取,持有偏向锁的线程永远不要同步。

加锁:锁对象第一次被线程获取时,把对象头标志为01, 偏向锁,cas把线程id记录在markword中,如果cas成功,持有偏向锁的线程每次将纳入同步块,不再进行任何同步,当另一个线程尝试获取锁的时候,偏向锁结束。

以上是关于内存模型的主要内容,如果未能解决你的问题,请参考以下文章

JVM内存模型Java内存模型 和 Java对象模型

深入理解Java内存模型

JMM内存模型JVM内存模型

JMM内存模型JVM内存模型

JMM内存模型

Java内存模型