一、JAVA内存模型
1 内存模型
目标:定义程序中 非栈中 各个变量的访问规则。
和计算机的内存模型有点相似,这只是一个内存模型,并非真实存在,相对于堆,栈内存区域,并不是同一层次的划分。
2 内存交互操作
lock: 主存
unlock: 主存
read: 从主存读到线程工作内存
write: 把store 后的变量放到主存中的变量中
load: 把read 的变量值放到工作内存副本中
store:工作内存 工作内存---->主存
use: 工作内存 --> 执行引擎
assign: 工作内存
read --> load :读到工作内存
store–> write :写到主存
3 指令重排与内存屏障
4 happens-before
保证多线程的执行结果不被改变。不限定重排。
- 程序顺序规则:一个Thread中的每个操作,happens-before于该线程中的任意后续操作。
- 监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁
- volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个域的的读
- 传递性:if A happens-before B,且 B happens-before C, 那么A happens-before C
- start()规则:如果Thread A 执行操作 ThreadB.start(),那么A线程中的ThreadB.start() happens-before 与线程B中的任意操作。
- join()规则:如果Thread A 执行操作 ThreadB.join() 并成功返回,那么B线程中的任意操作ThreadB.start() happens-before A线程 ThreadB.join() 后的操作。
1,5,6 联合使用
3 java与线程
线程的实现方式:
实现方式 | 特点 |
---|---|
使用内核线程(LWP) | 需要在用户态和内核态不停切换 每个LWP都需要一个内核线程支持,消耗内核资源 |
使用用户线程 | 用户线程的建立,同步,销毁,调度全部在用户态完成 进程:用户线程—1:N 没有内核支持,有阻塞危险 |
用户线程+ 轻量级进程(LWP) | 进程:用户线程—N:M |
java 线程实现
SunJDK windows 和 Linux 是一对一的线程模型
Solaris 一对一和多对多都支持
java 线程会映射到系统的原生轻量级进程,那么调度,切换都会有内核的开销。
线程的调度
同步式线程调度
抢占是线程调度
线程的状态转换
一、线程安全与锁优化
线程安全
共享数据类型 | eg |
---|---|
不可变(Immutable) | final ,String |
绝对线程安全 | 任何时候不需要其他同步 |
相对线程安全 | 单独操作是线程安全的(方法是线程安全?) Vector,HashTable |
线程兼容 | 对象本身不是线程安全,但是可以调用端用正确手段保证 ArrayList |
线程对立 | 无论调用端是否采取了同步,都无法线程安全 Thread的suspend() 和 resume() |
线程安全实现方法
1. 互斥同步(阻塞同步)
synchronized
moniterenter moniterexit 需要一个Reference 来指明要锁定和解锁的对象。
会阻塞线程,因为java线程映射到了操作系统的原生线程,如果要阻塞或者唤醒,都需要操作系统来,需要用户态到内核态的转换
2. 非阻塞同步
CAS
3. 无同步
可重入代码
先成本地存储:ThreadLocal
锁优化
1自旋锁与自适应自旋
-XX : +UseSpinnig
-XX : +PreBlockSpin
2 锁消除
基于JIT的逃逸分析
3 锁粗化
对一个对象重复,反复枷锁,可以粗化
4 轻量级锁
1.6之后 Mark World
5 偏向锁
-XX:-UseBiasedLocking
轻量级锁和偏向锁都是基于竞争不常发生的情况下进行的优化,如果都要膨胀成重量级锁,则没有必要。