volatile

volatile:

JVM 提供的轻量级的同步机制。保证了并发编程的可见性、有序性
不保证原子性

1、可见性:

定义:

有一个线程将主内存中的变量值做了修改,其他线程都将马上收到通知,立即获得最新值

  • 写线程:JMM会把该线程对应的本地工作内存中的共享变量值刷新到主内存。
  • 读线程:JMM会把该线程对应的本地工作内存置为无效,线程将到主内存中重新读取共享变量。
原理:

专业术语:

  • 内存屏障(memory barriers):一组处理器指令,用于实现对内存操作的顺序限制。
  • 缓存行(cache line):CPU高速缓存中可以分配的最小存储单位。处理器填写缓存行时会加载整个缓存行。
  • 借助了CPU的lock指令,lock指令在多核处理器下,
    1. 可以将当前处理器的缓存行的数据写回到系统内存,
    2. 同时使其他CPU里缓存了该内存地址的数据置为无效
案例演示:
public class VolatileDemo {
    volatile int number = 0;
    public static void main(String[] args) {
        VolatileDemo test1 = new VolatileDemo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"开始执行时,number = "+test1.number);
                try {
                    Thread.sleep(3000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                test1.add();//暂停3秒后,修改number的值。
                System.out.println(Thread.currentThread().getName()+"执行add()方法之后,number = "+test1.number);
            }
        },"t1").start();
        while (test1.number == 0){}
        System.out.println(Thread.currentThread().getName()+"程序结束!");
    }
    public  void add(){
            this.number = 10;
    }
}
  • 不加volatile,因为不在同一个线程内,一直陷入死循环
  • 加了volatile,此变量信息一旦改变,会通知其他线程获取新值
    • 将会把该线程对应的本地工作内存中的共享变量值刷新到主内存。
    • 会把读线程对应的本地工作内存置为无效,线程将到主内存中重新读取共享变量。

2、有序性:

防止指令重排

指令重排序:编译器和处理器重排序,

JMM会分别限制这两种指令重排序。(通过内存屏障)

介绍:

执行程序时,为了提高计算性能,编译器和处理器常常会对指令进行重排序,一般分为如下3种:

  • 源代码 ——> 编译器优化的重排 ——> 指令并行的重排 ——>内存系统的重排 ——> 最终执行的指令
  • 单线程环境下,可以确保程序最终执行结果和代码顺序执行结果的一致性(单线程环境下不用关注指令重排,因为是否重排都不会出错)。处理器在进行重排序时,必须要考虑指令之间的数据依赖性。
  • 多线程环境中,线程交替执行。由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果也就无法预测。
  • 用volatile关键字修饰的变量,可以禁止指令重排序,从而避免多线程环境下,程序出现乱序执行的现象。
解决方案-内存屏障:

volatile写操作:前后均加内存屏障

volatile读操作:在后面插入两个内存屏障。

  • 在每个volatile写操作的前面插入一个StoreStore屏障,防止写volatile与后面的写操作重排序。
  • 在每个volatile写操作的后面插入一个StoreLoad屏障,防止写volatile与后面的读操作重排序。
  • 在每个volatile读操作的后面插入一个LoadLoad屏障,防止读volatile与后面的读操作重排序。
  • 在每个volatile读操作的后面插入一个LoadStore屏障,防止读volatile与后面的写操作重排序。
内存屏障解释说明
StoreStore屏障禁止上面的普通写和下面的volatile 写重排序。
StoreLoad屏障防止上面的volatile 写与下面可能存在的volatile 读/写重排序
LoadLoad屏障禁止下面所有的普通读操作和上面的volatile读重排序。
LoadStore屏障禁止下面所有的普通写操作和上面的volatile读重排

3、不保证原子性:

原子性定义:

要么不做,要么全做

  • 当某个线程正在执行某件事情的过程中,是不允许被外来线程打断的。
  • volatile不能保证原子性,即执行过程中是可以被其他线程打断甚至是加塞的。
解决方案:
  1. 操作方法加synchronized,悲观锁机制,重量级
  2. 具体操作属性变成原子类,如AtomicInteger,乐观锁机制,值刷新的时候加锁
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值