JMM和Volatile初步学习笔记

Volatile的理解

volatile是java虚拟机提供的轻量级的同步机制
1.保证可见性
2.不保证原子性
3.禁止指令重排

JMM:java内存模型,不存在,是一个约定。

JMM同步的约定
1.线程解锁前:必须把工作内存中的变量刷新回主内存
2.线程加锁前:必须把主内存中的变量加载到自己工作内存中
3.加锁和解锁是同一把锁
在这里插入图片描述

内存交互操作

内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的
lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
write  (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中

JMM对这八种指令的使用,制定了如下规则:
不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write
不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存
不允许一个线程将没有assign的数据从工作内存同步回主内存
一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作
一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁
如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值
如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
对一个变量进行unlock操作之前,必须把此变量同步回主内存

以上是JMM主存与工作内存如何交互的,8中指令及规则。但是存在一个问题,如果线程A拿到主存中的flag,线程B也同样拿到,并且修改了falg的值。线程A不知道,出现了问题,代码演示如下
public class JMMDemo {
    private static boolean flag = true;
    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            while (flag){

            }
        }"A").start();
        TimeUnit.SECONDS.sleep(1);

        flag = false;
        System.out.println("结束");
    }
}

设置一个flag = true,开启一个线程进入循环,主线程修改flag的值为false,开启的线程则应该停止但是运行结果打印结束程序未停止。说明线程A对主线程是不可见的。

那么我们如何保证它的可见性呢?volatile
private volatile static boolean flag = true;

这样主线程修改flag的值,线程A就会停止循环

我们继续看volatile的不保证原子性
public class VolatileTest {
    private static int num = 0;
    public static void add(){
        num++;
    }
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            new Thread(()->{
                for (int j = 0; j < 100; j++) {
                    add();
                }
            }).start();
        }
        //主线程和GC垃圾回收线程为2,大于2线程礼让
        while (Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println(num);
    }
}

结果达不到1000,我们可以在方法上加锁,能够达到1000,去掉锁,在num上加关键字volatile,发现它还是达不到1000.说明它是没有原子性的,我们除了加锁外还有没有其他方法保证它的原子性呢?

使用原子类,atomicInteger.getAndIncrement()加一的操作,底层为CAS
public class VolatileTest {
    private static AtomicInteger atomicInteger = new AtomicInteger();
    public static void add(){
        atomicInteger.getAndIncrement();
    }
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            new Thread(()->{
                for (int j = 0; j < 100; j++) {
                    add();
                }
            }).start();
        }
        while (Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println(atomicInteger);
    }
}
禁止指令重排,内存屏障,CPU指令保证其顺序

编译器优化重排
并行指令重排
内存系统重排

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值