前言:
jvm
是java虚拟机用于解释,编译执行java代码,jvm可以再不同的操作系统上执行并且提供了内存管理,垃圾回收等
jmm
是java中多线程对于内存共享的行为规范,规定了在多线程环境下如何正确的使用共享变量,jmm定义了变量的可见性,原子性和有序性等特征,
引出线程的不可见性
- 众所周知,电脑只会有一个主内存和多个cpu,多个cpu想拿到主内存的数据,并不是直接去访问主内存的,需要通过cpu缓存去拿主内存的数据
图下所示:
- java多线程的内存标准和cpu缓存模型类似,是基于cpu缓存模型建立的
- 如下图所示:主内存中存储的是isflag=true,每一个线程的工作内存会生成一个副本isflag=true
- 如果线程1修改了值变为了isfalg=fase,那么他修改的对于另外一个线程是不可见的,另外一个线程取的是工作内存中的值
- 线程1的工作内存是isflag=false 线程2的工作内存中的值是isflag==true,线程之间是不可见的
- 代码演示
public class VolatileThread extends Thread{
public boolean isflag=true;
//public volatile boolean isflag=true;
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"子线程开始执行...");
while (isflag) {
}
System.out.println(Thread.currentThread().getName()+"子线程执行结束...");
}
public void setRuning(boolean isflag)
{
System.out.println("修改值");
this.isflag = isflag;
}
}
public class ThreadVolatile {
public static void main(String[] args) throws InterruptedException {
//实例化
VolatileThread volatileThread=new VolatileThread();
//启动线程
volatileThread.start();
Thread.sleep(3000);
//睡眠3s后,main线程调用修改状态的方法
volatileThread.setRuning(false);
}
}
- 线程1初始值是true,然后顺利的进入的死循环,然后 volatileThread.setRuning(false); 修改为false了,正常情况下程序应该结束,但是Thread-0读取的是副本的值,并不是直接读取的主内存的值,所以main线程修改的值,他是不可见的,如何解决这个问题?将isflag用volatile修饰
jmm的八个原子性操作
- 作可以确保多线程环境下的数据同步和一致性,保证对共享资源的操作是原子性的,避免了并发问题的发生。
- read(r读取):从主内存中读取数据
- load(载入)将主内存读取到的数据写入工作内存
- use(使用)从工作内存读取数据来计算
- assign(赋值)将计算好的值重新赋到工作变量中
- store(存储)将工作内存数据写入到主内存
- write(写入)将store过去的变量赋值给主内存
- lock(锁定)将主内存变量加锁,标识位线程独占状态
- unlock(解锁)将主内存变量解锁,解锁后其他线程可以锁定该变量
了解: - 缓存一致性协议
多个cpu从主内存读取同一个数据到各自高速缓存当其中的某个cpu修改了数据该数据会马上同步回主内存
,其他cpu会通过总线嗅探机制
可以感知到数据的变化,从而改变自己工作区间的数据 - 总线嗅探机制
-总线嗅探机制是一种硬件机制
,用于实现多个cpu共享数据的一致性
,当一个cpu修改了数据并将其写入到了主内存中,其他cpu通过监听
总线上的数据传输,可以感知到数据的变化。多个cpu从主内存读取同一个数据到各自高速缓存当其中的某个cpu修改了数据该数据会马上同步回主内存
,其他cpu会通过总线嗅探机制
可以感知到数据的变化,从而改变自己工作区间的数据 思考:为什么线程2修改了数据,线程1会知道,并同步数据,其过程是什么样子的?
- 原本isfllag-true 线程2通过store和write去修改isflag=false并写入到主内存中,需要经过数据总线,
因为总线嗅探机制
存在多个cpu去监控数据总线,当线程2修改数据经过数据总线的时候,被监控到了,其他cpu发现自己的工作空间也有isflag这个变量,就会失效
,当use使用的时候,发现失效了,找不到了就会重新去主内存加载,这就是数据同步
volatile 可见性
- 使用volatile 会使缓存一致性生效,只有缓存一致性生效,其他cpu才可以快速感知到数据的变化,从而让自己副本的数据失效,重新前往主内存拿数据
- 底层主要是通过汇编lock前缀指令,他会锁定这块区域的缓存(缓存行锁定),并写回主内存
volatile 实现顺序一致性
使用volatile的时候会加lock,在底层加上了lock的话就不会进行充排序了,这样就实现了顺序一致性(预计以后会写这个详细的)