volatile关键字的作用

volatile关键字的作用主要有以下两个作用

*内存可见性:当一个线程修改了一个共享变量时,另外一个线程能读到这个被修改的变量值。

*有序性:禁止指令重排序。

1、内存可见性

1.1、可见性问题

代码示例:


import java.util.Scanner;

/**
 * 观察内存可见性
 */
public class Exe_01 {
    public static class Counter{
        public static int count=0;
    }
    public static void main(String[] args) {
        Thread t1=new Thread(() ->{
            System.out.println(Thread.currentThread().getName()+"线程启动");
            while(Counter.count==0){
                //一直循环
            }
            System.out.println(Thread.currentThread().getName()+"线程退出");
        },"t1");
        //启动线程
        t1.start();
        //确保t1先启动
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Thread t2=new Thread(() ->{
            System.out.println(Thread.currentThread().getName()+"线程启动");
            //获取用户输入
            Scanner sc=new Scanner(System.in);
            System.out.println("请输入一个不为0的数字:");
            Counter.count=sc.nextInt();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"线程退出");
        },"t2");
        //启动线程
        t2.start();
    }
}

 

 

1.2、volatile解决内存可见性问题

添加volatile关键字修改代码


import java.util.Scanner;

/**
 * 观察内存可见性
 */
public class Exe_01 {
    public static class Counter{
        public static volatile int count=0;
    }
    public static void main(String[] args) {
        Thread t1=new Thread(() ->{
            System.out.println(Thread.currentThread().getName()+"线程启动");
            while(Counter.count==0){
                //一直循环
            }
            System.out.println(Thread.currentThread().getName()+"线程退出");
        },"t1");
        //启动线程
        t1.start();
        //确保t1先启动
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Thread t2=new Thread(() ->{
            System.out.println(Thread.currentThread().getName()+"线程启动");
            //获取用户输入
            Scanner sc=new Scanner(System.in);
            System.out.println("请输入一个不为0的数字:");
            Counter.count=sc.nextInt();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"线程退出");
        },"t2");
        //启动线程
        t2.start();
    }
}

 运行结果:

 

2、有序性(指令执行的有序)

2.1、内存屏障

作用就是保证指令的执行先后顺序

内存屏障
屏障类型指令示例说明
LoadLoadLoad1;LoadLoad;Load2保证load1的读操作先于laod2执行
StoreStoreStore1;StoreStore;Store2保证Store1的写操作先于Store2执行,并刷新到主内存
LoadStoreLoad1;LoadStore;Store2保证load1的读操作结束先于Store2的写操作执行
StoreLoadStore1;StoreLoad;Load2保证store1的写操作已刷新到主内存之后,load2及其后的操作才能执行

1、在每个volatile写操作之前插入StoreStore屏障,这样就能让其他线程修改变量A之后,让修改的新变量对当前线程可见。

2、在写操作之后插入StoreLoad屏障,这样就能让其他线程获取变量A的时候,能够获取到已经被当前线程修改的值

3、在每个volatile读操作之前插入LoadLoad屏障,这样就能让当前线程获取变量A的时候,保证其它线程也能获取相同的值,这样线程读取到的数据就一样了。

4、在读操作之后插入LoadStore屏障,这样就能让当前线程在其它线程修改变量A的值之前,获取到主内存里面变量A的值。

注:

volatile可以保证内存可见性,是因为在编译的过程中,在volatile修饰的变量前后都加入了相关的内存屏障

volatile可以保证有序性。

3、原子性 

 代码示例


public class Demo_402 {
    private static volatile int num=50000;
    public static void main(String[] args) throws InterruptedException {
        Counter04 counter=new Counter04();
        //创建两个线程
        Thread thread1=new Thread(()->{
            for (int i = 0; i < num; i++) {
                //自增操作
                counter.increase();
            }
        });
        Thread thread2=new Thread(()->{
            for (int i = 0; i < num; i++) {
                //自增操作
                counter.increase1();
            }
        });
        //启动线程
        thread1.start();
        thread2.start();
        //等待线程结束
        thread1.join();
        thread2.join();
        //获取自增后的count值
        System.out.println("count结果="+counter.count);
    }
}

class Counter04{
    public int count=0;
    //执行自增操作
    public void increase(){
        //在方法之前加上synchronized,修饰来说明这是一个需要加锁的方法
        count++;
    }
    //方法不加锁
    public void increase1(){
        count++;
    }
}

 运行结果:

 volatile关键字不保证原子性,原子性是由synchronized保证的,在多线程环境中一般两个关键字搭配使用。

 4、总结

1、volatile通过内存屏障,保证内存可见性。

2、volatile通过内存屏障,禁止指令重排序,从而保证有序性。

3、volatile不保证原子性。

*通过内存屏障,当一个线程修改了一个共享变量的时候,后面的线程就必须从主内存中重新读取最新的值

*通过内存屏障,让相邻的两条指令串行执行,从而保证内存可见性。

注意:volatile没有别的用法,就只是修饰变量

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值