线程可见性
JVM运行时数据区
Java内存模型(Java Memory Model ,JMM) VS JVM运行时数据区
初看Java内存模型
多线程中的问题
- 所见非所得
- 无法肉眼去检测程序的准确性
- 不同的运行平台有不同的表现
- 错误很难重现
eg 问题:(t1总的线程会一直执行下去为什么呢?jit对这种频繁调用的代码做了优化 会先取demo1.flag 赋值给一个局部变量 然后去判断这个局部变量的值 后面又详细的图)
工作内存缓存
CPU指令重排
JIT编译器(Just in time compiler)
脚本语言与编译语言的区别?
解释执行:即咱们说的脚本,在执行时,由语言的解释器将其一条条翻译成机器可识别的指令
编译执行:将我们写的程序,直接编译成机器可以识别的代码。
java是什么语言:
volatile关键字
可见性问题:让一个线程对共享变量的修改,能够及时的被其他线程看到
java内存模型规定:对volatile变量v的写入,与所有其他线程后续对v的读同步
要满足这些条件,所以volatile关键字就有这些功能:
- 禁止缓存:
volatile变量的访问控制符会加个ACC_VOLATILE—可以用javap -p -v aa,class进行查看
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.5 - 对volatile变量相关的指令不做重排序
public class VisibilityDemo {
// 运行标志
// public volatile boolean flag = true;
/*
*不带volatitle的时候thread1不会停止 jit 做了优化对于循环取值的时候
* boolean aa=demo1.flag
* if(aa){
* while(true){
* i++
* }
* }
*/
public boolean flag = true;
public static void main(String[] args) throws InterruptedException { //思维逻辑上看
VisibilityDemo demo1 = new VisibilityDemo();
System.out.println("代码开始了");
Thread thread1 = new Thread(new Runnable() {
public void run() {
int i = 0;
while (demo1.flag) {
i++;
}
System.out.println(i);
}
});
thread1.start();
TimeUnit.SECONDS.sleep(2); //JMM 必要性
// 设置is为false,使上面的线程结束while循环
demo1.flag = false;
System.out.println("被置为false了.");
}
}
线程间操作的定义:
- 线程间操作指:一个程序执行的操作可被其他线程感知或被其他线程直接影响。
- Java内存模型只描述线程间操作,不描述线程内操作,线程内操作按照线程语义执行
线程间操作有:
read操作(一般读,即非volatile读)
write操作(一般写,即非volatile写)
volatile read
volatile write
Lock.(锁monitor)、Unlock
线程的第一个和最后一个操作
外部操作
所有线程间操作,都存在可见性问题,JMM需要对器进行规范。
对于同步的规则定义
对于volatile变量v的写入,与所有其他线程后续对v的读同步
对于监视器m的解锁与所有后续操作对于m的加锁同步
对于每个属性写入的默认值(0,false,null)与每个线程对其进行的操作同步
启动线程的操作与线程中的第一个操作同步
线程T2的最后操作与线程T1发现线程T2已经结束同步。(isAlive,join可以判断线程时否终止)
如果线程T1中断了T2,那么线程T1的中断操作与其他所有线程发现T2被中断了同步
通过抛出InterruptedException异常,或者调用Thread.interrupted或Thread.isInterrupted.
Happens-before先行发生原则
happens-before关系用于描述两个有冲突的动作之间的顺序,如果一个action happens before 一个action,则第一个操作被第二个操作可见,JVM需要实现如下happens-before规则:
某个线程中的每个动作都happens-before 该线程中该动作后买面的炒作
某个线程上的unlock动作happens-before同一个线程上后续的lock动作
对某个volatile字段的写操作happens-before每个后续对该volatile字段的读操作
在某个线程对象上调用start()方法happens-before被启动线程中的任意动作
如果在线程t1中成功的执行了t2.join(),则t2中所有操作对t1可见
如果某个动作a happens-before动作b,且b happens-before动作c,则有a happens-before c
当程序包含两个没有被happens-before关系排序的冲突访问时,就称存在数据竞争
遵守了这个规则,也就意味着有些代码不能进行重排序,有些数据不能缓存