十、volatile

一、原理

  • 底层实现是内存屏障,Memory Barrier(Memory Fence)

1. 可见性

1.1 写屏障(sfence)
  • 被voliate修饰的变量,会在写操作后,加上写屏障
  • 在该屏障之前的所有改动,都会同步到主存中去
package com.dreamer.multithread.day02;

public class Demo03 {

    private static int number = 0;

    private static volatile int age = 0;

    public static void main(String[] args) {

        new Thread(() -> {
            /**
             * 1. age被volatile修饰,因此会在后面加上写屏障
             * 2. 写屏障作用:将上面的所有改动,都会同步到主存中去
             */
            number++;
            age++;
            // JVM  会加上写屏障
        }).start();

    }
}
1.2 读屏障(sfence)
package com.dreamer.multithread.day02;

public class Demo03 {

    private static int number = 0;

    private static volatile int age = 0;

    public static void main(String[] args) {
        
        new Thread(() -> {

            /**
             * 1. age被volatile修饰,因此在前面加上读屏障
             * 2. 读屏障后面的所有的操作,都会在主存中去取
             */
            // JVM 会加上读屏障
            int numberResult = number;
            int ageResult = age;
        }).start();

    }
}

2. 有序性

  • 防止指令重排
  • 只能针对本线程内不会发生指令重排
2.1 写屏障(sfence)
  • 写屏障会将保证屏障上面的代码不会重排,也就是说不会将屏障上面的代码挪到屏障下面执行
2.1 读屏障(sfence)
  • 上述一样的道理

3. 不能保证指令交错

  • volatile不能保证不同线程在执行的时候的指令交错引发的问题
  • 因为它只是保证各个线程在执行的时候都从主存中去加载
  • 保证指令不会发生交错: 用synchronized来进行枷锁

二、happen-before原则

  • 规定了对共享变量的写操作,对其他线程的读操作的可见性总结
  • 是可见性和有序性的一套总结

1. synchronized

  • 线程解锁m之前对变量的写,对于接下来的用m加锁的其他线程的读,改变是可见的
package com.dreamer.multithread.day02;

public class Demo04 {
    private static int x = 0;

    private static Object lock = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (lock) {
                x = 10;
            }
        }).start();

        new Thread(() -> {
            synchronized (lock) {
                System.out.println(x);
            }
        }).start();
    }
}

2. volatile

  • 变量用volatile修饰,一个线程对其的修改,对于其他线程来说是可见的
package com.dreamer.multithread.day02;

public class Demo05 {
    private static volatile int x = 0;

    public static void main(String[] args) {
        new Thread(() -> x = 10).start();

        new Thread(() -> System.out.println(x)).start();
    }
}

3. 起步

  • 线程start前对变量的写操作,对该线程开始后的读操作是可见的
package com.dreamer.multithread.day02;

public class Demo06 {
    private static int x = 0;

    public static void main(String[] args) {

        x = 10;

        new Thread(() -> System.out.println(x)).start();
    }
}

4

  • 线程结束前对变量的写操作,对其他线程得知它结束后的读操作可见性(如调用join方法)
package com.dreamer.multithread.day02;

public class Demo07 {

    private static int x = 0;

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

        Thread thread = new Thread(() -> x = 10);
        thread.start();

        thread.join();

        System.out.println(x);
    }
}

5. 默认值

  • 对变量默认值的写,0,false,null,其他线程对该变量可见

6. 传递性

  • 其实就是volatile的读屏障和写屏障问题
package com.dreamer.multithread.day02;

public class Demo08 {
    private static int x = 0;

    private static volatile int y = 0;

    public static void main(String[] args) {
        new Thread(() -> {
            x = 10;
            y = 20;
            // 写屏障,会将上面的操作全部赋值到主存中去
        }).start();

        new Thread(() -> {
            System.out.println(x);
            System.out.println(y);
        }).start();
    }


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值