认识volatile

北战南征 提交于 2019-12-25 19:39:02

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

volatile关键字不用多说,就是保证内存可见性,被它修饰的变量在被线程改变之后会马上同步回主内存(这句话实际有点问题,并不是会立马同步,而是在缓存行没空间或者遵循最晚时间原则),但是肯定能确保其他线程读到的是最新的值,这里直接上demo了(不BB那些没用的):

package unsafe;


/**
 * @Authror ayo
 * @Date 2019/12/25 14:39
 */
public class VolatileDemo {

    /**
     * 闹钟是否响铃(true代表响铃,false代表没响铃)
     */
    private static boolean isClockRang = false;

    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                //如果闹铃不响我就一直睡觉
                while (!isClockRang){
                    
                }
                System.out.println("起床了");
            }
        }, "Thread1").start();
        //休眠一秒钟,让线程1先跑起来
        Thread.sleep(1000);
        new Thread(new Runnable() {
            @Override
            public void run() {
                isClockRang = true;
            }
        }, "Thread2").start();
        //休眠一秒钟,让线程2把改变的值同步回主内存
        Thread.sleep(1000);
        System.out.println("闹钟是否响铃: " + isClockRang);
    }
}

运行上面代码,得到如下结果:

都响铃了为什么不起床?很简单,没用volatile修饰嘛,加上试试(将isClockRang加上volatile修饰即可):

volatile起作用了,这没什么可研究的,但是我将代码稍微改点,volatile还去掉,另外打印一句话:

package unsafe;


/**
 * @Authror ayo
 * @Date 2019/12/25 14:39
 */
public class VolatileDemo {

    /**
     * 闹钟是否响铃(true代表响铃,false代表没响铃)
     */
    private static boolean isClockRang = false;

    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                //如果闹铃不响我就一直睡觉
                while (!isClockRang){
                    System.out.println("我不用volatile也行啊");
                }
                System.out.println("起床了");
            }
        }, "Thread1").start();
        //休眠一秒钟,让线程1先跑起来
        Thread.sleep(1000);
        new Thread(new Runnable() {
            @Override
            public void run() {
                isClockRang = true;
            }
        }, "Thread2").start();
        //休眠一秒钟,让线程2把改变的值同步回主内存
        Thread.sleep(1000);
        System.out.println("闹钟是否响铃: " + isClockRang);
    }
}

运行如下:

也可以啊,有疑问的按住ctrl点进System.out.println("我不用volatile也行啊")这句话中的println方法,你看见什么了?

public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }

这就是println的源码,很明显这个方法内部加了synchronized,加了synchronized意味着会有线程上下文切换,当线程1失去cpu使用权然后重启获取cpu使用权的时候,肯定要从主内存重新load变量isClockRang的嘛,原因就在这了。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!