volatile关键字作用及Dome

        关于volatile关键字的作用,网上有很多解释和dome,但是有一些Dome是运行后达不到效果的。我亲试并整理了下面两个Dome留做笔记。

本文连接:volatile关键字作用及Dome_张三的博客-CSDN博客

volatile的作用1.保证数据的可见性。2防止指命重排序。备注:不能保证原子性

1.保证数据保见性

1.1为什么会有可见性的问题?因为共享对象是共享内存中(这里理解的是放到方法区或堆),每个线程使用的时候会将对象拿到线程内存中(线程栈)。加入volatile关键字后,线程会每次使用前去共享内存中拿,做完写操作也会及时的写入,从而保证数据的可见性。附带一个Dome。

图来自百度百科

volatileå³é®å­çä½ç¨

1.2Dome解释:

不加volatile时,子线程修改了flag的状态后,主线程取不到最新变量,会一直什么都不打印
加volatile时,子线程修改了flag的状态后,主线程取得到最新变量,会一直打印flag is true

Dome可以去掉volatile关键字来试一下结果。

package com.example.demo.jvm;


public class VolatileVisible {
    /**
     *全局变量每个线程都能用
     */
    private static /*volatile*/ boolean flag = false;

    /**a
     * 不加volatile时,子线程修改了flag的状态后,主线程取不到最新变量,会一直什么都不打印
     * 加volatile时,子线程修改了flag的状态后,主线程取得到最新变量,会一直打印flag is true
     * @param args
     */
        public static void main(String[] args) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                    }
                    flag = true;
                    System.out.println("flag=" + flag);
                }
            }).start();
            while (true) {
                if (flag) {
                    System.out.println("flag is true");
                }
            }
        }
}


2防止内存的重排序 

2.1什么是重排序:CPU为了提升性能,有时候会对我们的指命进行重排序,当时是指的未相互引用同样数据的指令,否则就结果问题。比如我们执行一个 User user = new User();User构造方法有一句打印语句。这时候我们的指令会被拆分成1.new 内存空间,2执行构造方法中的打印语句,3赋值给user对象。这时候CPU为了性能可能执行的是1,3,2这样的语句。如果是单线程那这样问题不大,但是在多线程共享的对象的话则会产生问题,因为其它线程会认为user被赋值的时候构造函数已经执行完了。这时就发生了指命的重排序,这时候我们就需要加入volatile。

2.2Dome解释 

* 两个线程,每个线程操作两个变量。t11代表线程1的第一个变量,以此类推。
* 如果出现t12和t22同时为零则说明cpu没有按编写顺序去执行,即cpu发生了重排序。
* 代码逻辑解释:
* 先以cpu不会乱序来看,
* t12为0时,说明线程1执行完第二句话时,线程2还没有执行,这时线程2的代码去执行.
* 以这种顺序来看,t22必然不能为0。如果为0则说明cpu没有按我们预想的顺序去执行,即发生了重排序。

我们不带voliatile时,执行一段时间会出现重排序,加入voliatile后不会出现。可以用代码中的注释调试一下。

package com.example.demo.jvm;

public class VolatileReOrder {
    /**
     * 两个线程 各两个变量
     */
    private static /*volatile*/ int t11 = 0,t12 = 0, t21 = 0,t22 = 0;

    public static void main(String[] args) throws InterruptedException {
        cpuReOrder();
    }

    /**
     * 两个线程,每个线程操作两个变量。t11代表线程1的第一个变量,以此类推。
     * 如果出现t12和t22同时为零则说明cpu没有按编写顺序去执行,即cpu发生了重排序。
     * 代码逻辑解释:
     * 先以cpu不会乱序来看,
     * t12为0时,说明线程1执行完第二句话时,线程2还没有执行,这时线程2的代码去执行.
     * 以这种顺序来看,t22必然不能为0。如果为0则说明cpu没有按我们预想的顺序去执行,即发生了重排序。
     * @throws InterruptedException
     */
    private static void cpuReOrder() throws InterruptedException {
        int i = 0;
        for(;;){
            i++;
            t12=0;t11=0;t21=0;t22=0;//恢复变量
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    t11 = 1;
                    t12 = t21;
                }
            });
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    t21 = 1;
                    t22 = t11;
                }
            });
            t1.start();t2.start();
            t1.join();t2.join();//等待执行完毕
            if(t12 == 0 && t22 == 0){
                System.out.println("出现重排序i="+i);
                break;
            }
        }
    }
}

重排序比较难复现,去掉voliatile关键字执行很长时间才行。截图:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值