AQS系列之起始篇-volatile和unsafe理解和使用

我来填坑,第一篇先不讲AQS,打算先说清楚AQS的一些相关知识。这样后续再看AQS会比较容易理解;如果内容有误,麻烦留言斧正,有疑问请关注公众号私信交流共同成长!
这一篇主要讲vloatile关键字和Unsafe类。

volatile 关键字

我们都知道volatile关键字,是通过内存屏障实现了两个特性:

  • 可见性:假设两个线程A和B同时从主内存中读取了同一个变量c,线程A修改了变量c,会及时写回主内存,并且使线程B持有的变量c失效,然后线程B从主内存读取到被线程A修改过的变量。这样就保证了可见性;
  • 避免指令重排序,这个就是在volatile变量前后加了内存屏障,可以当成一个标识,在这个标识前后的指令,不能进行重排序。
可见性

来看第一段代码,证明一下可见性:

public class VolatileTest2 {
    private static volatile Integer c = 0;

    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            while (true) {
                if (c == 0) {
                    System.out.println("ac = " + c);
                } else {
                    System.out.println("bc = " + c);
                    break;
                }
            }

        }).start();

        new Thread(() -> {
            c = 1;
        }).start();
        
    }
}

运行结果:

ac = 0
ac = 0
ac = 0
ac = 0
ac = 0
ac = 0
ac = 0
ac = 0
bc = 1

同时起了两个线程,一个线程对volatile变量的修改,对于另一个线程来说是可见的。

避免重排序

第二段代码,想说明的是volatile修饰的变量不仅影响变量自身,而且会影响他前后变量。这个也就是volatile会避免指令重排序这个特性的解释:

public class VolatileTest4 {
    // a不使用volatile修饰
    public static long a = 0;
    // 消除缓存行的影响
    public static long p1, p2, p3, p4, p5, p6, p7;
    // b使用volatile修饰
    public static volatile long b = 0;
    // 消除缓存行的影响
    public static long q1, q2, q3, q4, q5, q6, q7;
    // c不使用volatile修饰
    public static long c = 0;

    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            while (a == 0) {
                try {
//                    long x = b;
                } catch (Exception ex) {
                }
            }
            System.out.println("a=" + a);
        }).start();

        new Thread(() -> {
            while (c == 0) {
                try {
                    long x = b;
                } catch (Exception ex) {
                }
            }
            System.out.println("c=" + c);
        }).start();
        Thread.sleep(100);
        a = 1;
        b = 1;
        c = 1;
        System.out.println("main end");
    }
}

运行结果:

main end
c=1

上面提到的消除缓存行的影响,如果感兴趣可以搜索关键词“伪共享”去了解。
再说回这段代码:

  1. 同时启动两个线程,然后主线程休眠100毫秒,这个主要为了保证两个子线程都已经启动了。
  2. 主线程分别修改abc三个变量的值,b是volatile修饰的,所以两个子线程是可见这个这个变量的改变的
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值