Volatile

Volatile是Java虚拟机提供轻量级的同步机制

  1.  保证可见性
  2. 不保证原子性
  3. 禁止指令重排

可见性

        可见性与Java的内存模型(JMM)有关,模型采用缓存与主存的方式对变量进行操作,也就是说,每个线程都有自己的缓存空间,对变量的操作都是在缓存中进行的,之后再将修改后的值返回到主存中,这就带来了问题,有可能一个线程在将共享变量修改后,还没有来的及将缓存中的变量返回给主存中,另外一个线程就对共享变量进行修改,那么这个线程拿到的值是主存中未被修改的值,这就是可见性的问题。

        volatile很好的保证了变量的可见性,变量经过volatile修饰后,对此变量进行写操作时,汇编指令中会有一个LOCK前缀指令,这个不需要过多了解,但是加了这个指令后,会引发两件事情:

  1. 将当前处理器缓存行的数据写回到系统内存
  2. 这个写回内存的操作会使得在其他处理器缓存了该内存地址无效

意思就是说当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值,这就保证了可见性。

eg://没有使用volatile,Thread1线程对主内存中的变价不知道,加了volatile可以保证可见性


package lin.jmm;

import java.util.concurrent.TimeUnit;

public class Test01 {
    private static int num=0;
    public static void main(String[] args) {
        new Thread(()->{
            while (num==0){

            }
        },"Thread1").start();

        //停一秒,使Thread1线程启动
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        num=1;
        System.out.println(num);
    }
}

 

eg://使用volatile

package lin.jmm;

import java.util.concurrent.TimeUnit;

public class Test01 {
    private volatile static int num=0;
    public static void main(String[] args) {
        new Thread(()->{
            while (num==0){

            }
        },"Thread1").start();

        //停一秒,使Thread1线程启动
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        num=1;
        System.out.println(num);
    }
}

原子性(不可分割)

        首先需要了解的是,Java中只有对基本类型变量的赋值和读取是原子操作,如i = 1的赋值操作,但是像j = i或者i++这样的操作都不是原子操作,因为他们都进行了多次原子操作,比如先读取i的值,再将i的值赋值给j,两个原子操作加起来就不是原子操作了。

        线程A在执行任务的时候,不能被打扰,不能被分割,要么同时成功,要么同时失败。

        所以,如果一个变量被volatile修饰了,那么肯定可以保证每次读取这个变量值的时候得到的值是最新的,但是一旦需要对变量进行自增这样的非原子操作,就不会保证这个变量的原子性了。
 

package lin.volataile_Test;

//不保证原子性
public class Test02 {
    private volatile static int sum=0;
    public static void add(){
        sum++;
    }
    public static void main(String[] args) {
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {  //正常sum为2万
                    add();
                }
            }).start();
        }

        while (Thread.activeCount()>2){
            Thread.yield();
        }

        System.out.println(Thread.currentThread().getName()+" "+sum);
    }
}

加了synchronized 或 lock锁,就可以保证原子性

 如果不使用 lock 和 synchronized,怎么保证原子性?

        使用原子类,解决原子性问题

package lin.volataile_Test;

import java.util.concurrent.atomic.AtomicInteger;

//不保证原子性
public class Test02_AtomicInteger {
    private volatile static AtomicInteger sum = new AtomicInteger();
    public static void add(){
        //sum++; 因为是一个类,所以不能直接++操作
        sum.getAndIncrement();  //AtomicInteger 的+1方法:  CAS

    }
    public static void main(String[] args) {
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {  //正常sum为2万
                    add();
                }
            }).start();
        }

        while (Thread.activeCount()>2){
            Thread.yield();
        }

        System.out.println(Thread.currentThread().getName()+" "+sum);
    }
}

 这些类的底层都是直接和操作系统挂钩!在内存中修改值!Unsafe类是一个很特殊的存在!

指令重排

什么是指令重排?

        源代码-->编译器优化的重排-->指令并行也可能会重排-->内存系统也会重排-->执行

        处理器在进行指令重排的时候,会考虑数据之间的依赖性

volatile可以避免指令重排问题:

        内存屏障。CPU指令。作用:

                1、保持特定的操作的执行顺序!

                2、可以保证某些变量的内存可见性(利用这些特性volatile实现了可见性)         

       

 只要加了volatile就会在上面跟下面加一个内存屏障

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lin_XXiang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值