Volatile&原子性&一致性&JVM&指令重排

80 阅读3分钟

概念

  • Volatile概念:Volatile关键字的主要作用是使变量在多个线程间可见。
  • 作用: 在多线程间可以进行变量的变更,使得线程间进行数据的共享可见 阻止指令重排序,happens-before
package com.example.core.cas;

import com.example.core.safely.UseThreadLocal;

public class UseVolatile extends Thread{
    private volatile boolean isRunning = true;

    private void setRunning(boolean isRunning){
        this.isRunning = isRunning;
    }

    public void run(){
        System.out.println("进入Run方法");
        while(isRunning == true){
            //...
        }
        System.err.println("线程停止");
    }

    public static void main(String[] args) throws InterruptedException{
        UseVolatile uv = new UseVolatile();
        uv.start();
        Thread.sleep(2000);
        uv.setRunning(false);

        System.out.println("isRunning的值已经被设置为false");
    }
}
  •  一个线程可以执行的操作有使用(use)、赋值(assign)、装载(load)、存储(store)、锁定(lock)、解锁(unlock)。
  • 而主内存可以执行的操作有读(read)、写(write)、锁定(lock)、解锁(unlock),每个操作都是原子的。
  • volatile的作用就是强制线程到主内存(共享内存)里去读取变量,而不去线程工作内存区里去读取,从而实现了多个线程间的变量可见。也就是满足线程安全的可见性

JVM

  • Java Memory Model(Java 内存模型),简称JMM,并发编程这块,解决一个线程对共享变量的写入何时对另一个线程可见!
  • 比如一个线程给变量 a 赋值 int a = 3; // 向变量 a 写值 我要解决的问题就是:"在什么条件下,读取变量a的线程将看到这个值3",如果缺少同步,那会有很多因素使得读取变量a的线程不能立即看到或者永远看不到这个值3
  • 所有的变量都存储在主内存中, 每一个线程都有一个私有的本地内存,本地内存中存储了该线程使用到的变量在主内存中拷贝! 线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量(volatile变量也不例外)
  • 如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系,A=1;B=A;就是A操作的结果要对B操作可见,那么必然存在A happens-before B 简而言之:使用happens-before的概念来阐述操作之间的内存可见性
  • 例如:对一个锁的解锁,happens-before 于随后对这个锁的加锁,先后顺序很关键

指令重排

  • JAVA语言为了维持顺序内部的顺序化语义,也就是为了保证程序的最终运行结果需要和在单线程严格意义的顺序化环境下执行的结果一致,程序指令的执行顺序有可能和代码的顺序不一致,这个过程就称之为指令的重排序
  • 指令重排序的意义在于:JVM能根据处理器的特性,充分利用多级缓存,多核等进行适当的指令重排序,使程序在保证业务运行的同时,充分利用CPU的执行特点,最大的发挥机器的性能
  • volatile可以阻止指令的重排序
  • 涉及到cpu底层的硬件对于语言的编写