主内存与工作内存
java内存模型规定所有的变量(实例字段,静态字段,数组元素)都存储在主内存中。每条线程有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝。线程对变量的所有操作都必须在工作内存中完成,不能直接读写主内存。
内存间交互操作
java定义了8中操作来完成主内存与工作内存的交互
- lock: 锁定,作用于主内存的变量把一个变量标志为一条线程独占
- unlock:解锁,作用与主内存的变量,把标志为锁定的变量释放出来
- read:读取,作用于主内存变量,把一个变量的值从主内存传输到工作内存
- load:载入,作用于工作内存,把read到的值放入工作内存的变量副本中
- use:使用,作用于工作内存,把工作内存的变量传递给执行引擎
- assign:赋值,作用与工作内存,把执行引擎接收到的值赋值给工作内存的变量
- store:作用于工作内存,把工作内存中的一个变量传递到主内存
- write:作用于主内存,把store操作中的变量值方放到主内存的变量中
valatile
java 虚拟机提供的轻量级的同步机制,
- 当一个变量定义为valatile,保证此变量对所有线程的可见性,但必不能保证在并发下是安全的
- 可以禁止指令重排
long,double型变量的特殊规则
允许JVM将没有被valatile修饰的64位数据的读写操作划分为2次32位的操作来进行
- 原子性:java内存模型保证的原子性变量操作包括:read,load,assign,use,store和write,可以认为基本的数据类型的访问读写是具备原子性的。jvm提供了monitorenter和monitorexit字节码指令之间的操作具有原子性,也就是synchronized代码块
- 可见性:指当一个线程修改了共享变量的值,其它线程能够立即得知这个修改。valatile在变量修改后立即同步回主内存,在变量读取前立即从主内存刷新变量值。synchronized和final修饰的字段也能实现可见性。
- 有序性:
先行先发生原则,如果2个操作之间无法从下列规则推导出来,则JVM可以对他们随意的排序
- 程序次序规则
- 管程锁定规则
- valatile变量规则
- 线程启动规则
- 线程停止规则
- 线程终止规则
- 线程中断规则
- 对象终结规则
- 传递性
java与线程
- 使用内核线程实现
- 使用用户线程实现
- 使用用户线程加轻量级进程混合实现
使用抢占式线程调度
线程的状态
- 新建(New)
- 运行(Runnable)
- 无限期等待(Waitint)
- 限期等待(Timed Waiting)
- 阻塞(blocked)
- 接收(Terminated)
java操作共享数据分为5类
- 不可变,final关键字修饰
- 绝对线程安全,不管运行时环境如何,调用者都不需要额外的同步措施
- 相对线程安全,我们通常意义上所讲的线程安全,对象单独操作时线程安全的
- 线程兼容,值对象本身并不是线程安全的,但可以通过在调用端正确地使用同步手段在并发环境中可以安全的使用
- 线程对立,指无论调用端是否采取了同步措施,都无法再多线程环境中并发使用
线程安全的实现方法
- 互斥同步:同步是指保证共享数据在同一时刻只被一个(或一些,使用信号量的时候)线程使用。互斥是实现同步的一种手段,临界区,互斥量和信号量等方式。最基本的互斥同步手段就是synchronized关键字,执行monitorenter指令时先尝试获取对象的锁,如果没被锁定,或当前线程以及有该锁,则锁的计数器+1,相应的执行monitorexit指令时-1,当计数器为0时,锁就释放。
JUC包下的ReentrantLock与synchronized区别:
- 一个表现为API层面,一个表现为元素语法层面
- ReentrantLock增加了一些高级功能,等待可中断,公平锁,锁绑定多个条件
- 非阻塞同步:基于冲突检测的乐观锁并发策略,就是先进行操作,如果没有其他线程争用共享数据,那就操作成功,如果有,产生了冲突,那就采取补偿措施(不断重试),CAS依赖底层指令实现,存在“ABA"逻辑漏洞问题。
- 无同步方案
可重入代码:不依赖存储在堆上的数据和公用资源,如果一个方法,他的返回结果是可预测的,只要相同的数据,就都能返回相同的结果,那它就满足可重入性的要求。
线程本地存储:将共享数据的可见范围限制在一个线程内,每一Thread对象中都有一个ThreaLocalMap对象,存储了一组以ThreadLocal.threadLocalHashCode为键,以本地线程变量为值的K-V值对。
锁优化
- 自旋锁与自适应自旋
互斥同步对性能最大的影响是阻塞的实现,挂起线程和恢复线程给操作系统的并发性能带来了很大的压力,为了让线程等待,只需让线程执行一个忙循环(自旋),这项技术就是自旋锁。自适应自旋是根据前一次在同一个锁上的自旋时间以及锁的拥有状态来决定的。 - 锁消除
指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除 - 锁粗化:把加锁同步范围扩展
- 轻量级锁
并不是用来代替重量级锁,它的本意是在没有多线程竞争的前提下,减少传统重量级锁产生的性能消耗。虚拟机首先将当前线程的栈帧中建立一个锁记录(Lock Record)空间,用于存储对象目前Mark Word的拷贝,然后JVM使用CAS操作尝试将对象的Mark Word更新为指向Locked Record的指针 - 偏向锁:在无竞争的情况下把整个同步都消除掉。当锁对象第一次被线程获取的时候,JVM把对象头中的标志设为”01“,同时使用CAS操作把这个锁的线程ID记录在对象的MW中,如果CAS成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时,JVM可以不再进行任何同步操作。
网友评论