volatile的关键字

文章通过示例展示了volatile关键字在Java中的作用,包括保证可见性和禁止指令重排,但不保证原子性。针对原子性问题,文章推荐使用AtomicInteger等JUC工具类来解决,并解释了多线程环境下编译器优化重排可能导致的不确定性。
摘要由CSDN通过智能技术生成

volatile是Java虚拟提供的轻量级同步机制,具有可见性,不保证原子性,禁止指令重排的特点。

class MyData{
    volatile int number = 0;

    public void addT060(){
        this.number = 60;
    }
    //此时number前面是加了volatile关键字修饰,但它不保证原子性
    public void addPlus(){
        number++;
    }

    AtomicInteger atomicInteger = new AtomicInteger();//AtomicInteger(1),括号里面不写值默认为0
    public void addMyAtomic(){
        atomicInteger.getAndIncrement();
    }
}

/**
 * 1、验证volatile的可见性
 * 1.1、加入 int number = 0;number 变量之前根本没有添加volatile的关键字修饰,没有可见性
 * 1.2、添加了volatile,可以解决可见性问题
 *
 * 2、验证volatile不保证原子性
 * 2.1、原子性指的是什么意思?
 *      不可分割,完整性,也即某个线程正在做某个具体业务时,中间不可以被加塞或者被分割。需要整体完整,要么同时成功,要么同时失败。
 * 2.2、volatile不保证原子性
 * 2.3、为什么不保证原子性
 * 2.4、如何解决原子性
 *      · 加syn
 *      · 使用juc下的Atomic
 */
public class VolatileDemo {

    public static void main(String[] args) {
        MyData myData = new MyData();

        for (int i = 0; i < 20; i++){
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    myData.addPlus();
                    myData.addMyAtomic();
                }
            },String.valueOf(i)).start();
        }

        //需要等待上面20个线程都全部计算完成后,在用main线程取得最终的结果只看是多少
        while(Thread.activeCount() >2){
            Thread.yield();
        }

        //最后正确的值应该是20000,但最终结果不是20000,因此可以看出volatile关键字不保证原子性
        System.out.println(Thread.currentThread().getName() + "\t finally number value: " + myData.number);
        System.out.println(Thread.currentThread().getName() + "\t finally number value: " + myData.atomicInteger);
    }

    //volatile可以保证可见性,即使通知其他线程,著物理内存的值已经被修改
    private static void seeOkByVolatile() {
        MyData myData = new MyData();
        new Thread(()->{
            System.out.println(Thread.currentThread().getName() + "\t come in");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myData.addT060();
            System.out.println(Thread.currentThread().getName() + " update number value:" + myData.number);
        },"AAA").start();

        //第二个线程就是我们的main线程
        while(myData.number == 0){

        }

        System.out.println(Thread.currentThread().getName() + " mission is over," + Thread.currentThread().getName() + " get number value:" + myData.number);
    }
}

volatile 具有可见性,但它不保证原子性,解决原子性的问题时,我们优先选择JUC锁的Atomic,synchronized是重量级锁,一般情况下不优先使用,上面的例子可以用Atomic解决,我们就用Atomic解决它的原子性问题。

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

  • 单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致。
  • 处理器在进行重排序是必须要考虑指令之间的数据依赖性。
  • 多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

鲤鱼程序员

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

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

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

打赏作者

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

抵扣说明:

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

余额充值