java内存模型与线程
java内存模型
-
java的工作内存方式与CPU、缓存、内存间的关系很类似,如下:
java线程<—>工作内存<—>|Save |
java线程<—>工作内存<—>|和load |<—>主内存
java线程<—>工作内存<—>|操作 |
所有变量都在主内存中,工作内存中存储要使用的变量在主内存中的副本,线程对变量所有操作都在工作内存中,无法直接访问主内存。
-
内存交互操作包含
lock: 将主内存变量标志为某个线程独占
unlock: 去除lock锁定效果
read: 变量由主内存到线程工作内存-------------必须配合使用
load: 配合read在工作内存生成变量副本-----------/\
use: 工作内存内一个变量传输给执行引擎
assign: 给工作内存中变量赋值
store: 把变量从工作内存传到主内存。---------必须配合使用
write: 配合store,将变量写入主内存。------------/\
使用规则P443
-
volatile变量
它有两重含义:—被修饰变量保持对所有线程的可见性,修改了后,其他线程肯定能第一时间看到;
—禁止指令重排序优化,例子P447-448。
特别是第一重含义并不能保证变量的同步,被vollatile修饰的变量只适合用来做标志位,不适合用作运算操作。注意:即使编译出来只有一条字节码,不意味着是原子操作。
假设V、W是两个被volatile修饰的变狼,回归到操作层面:每次使用V前都必须从主内存刷新;每次修改V后都立即同步到主内存;P450(不会指令重排序)
-
原子性、可见性、有序性
原子性:除了lock、unlock其他都是原子操作
可见性:volatile、synchronized、final都能实现
有序性:volatile、synchronized实现。
synchronized很万能,但很影响性能。
-
先行发生原则
操作A先行发生与操作B,意思是在操作B之前,操作A的影响能被B观察到。时间先后与先行发生原则没关系。
程序次序规则:一个线程内按照控制流顺序发生。
管程锁定:unlock发生在同一个锁的ock之前
volatile规则:对volatile变量的写操作发生在后边的读操作之前。
线程启动规则:start()先于线程任何动作。
线程终止规则:线程所有操作都先于线程的终止检测。
线程中断规则:线程的interrupt()方法调用先于被中断线程代码检测到中断发生。
对象终结规则:对象的构造函数结束先于其finalize()方法
传递性:A先于B,B先于C,则A先于C
java与线程
-
线程的实现
编程语言实现线程的方式有很多:直接与内核线程进行1:1映射,完全使用用户线程(所有的线程操作都需要用户自己搞定),混合实现。
主流jvm使用第一种方式实现线程。
-
线程调度
分为协同式与抢占式,java使用抢占式,java设定的优先级不一定会被OS接受。
线程状态包含:新建New,运行Runnable,无限期等待Waiting,限期等待Timed Waiting,阻塞Blocked,结束Terminated
New<----------------start()------------->| Running |<---------synchronized----->Blocked
| |<----------sleep()------->Timed Waiting
Waiting<------wait(),notify()-------->| |<------run()结束----------->Terminated
java与协程
- 内核线程存在局限:java web服务器线程池通常有几十到两百个线程,当有数百万计的请求访问时,线程切换消耗太大。
- 协程应运而生,就是协同式调度用户模拟线程,它更轻量,java实现暂时没有,有个未发布项目:Loom。