性能优化之@Contended减少伪共享

举报
西魏陶渊明 发表于 2022/09/25 00:32:43 2022/09/25
【摘要】 作者: 西魏陶渊明 博客: https://blog.springlearn.cn/ 西魏陶渊明 莫笑少年江湖梦,谁不少年梦江湖 一、什么叫伪共享 说到伪共享,就要说CPU缓存,我们程...

作者: 西魏陶渊明
博客: https://blog.springlearn.cn/

西魏陶渊明
莫笑少年江湖梦,谁不少年梦江湖

一、什么叫伪共享

说到伪共享,就要说CPU缓存,我们程序执行时候信息会被保存到CPU缓存中
而这些缓存中的数据可能被多线程访问,假如一个线程还没处理完,另外一个线程
就对数据进行了修改,就会导致上一个线程发生幻读的情况,比如刚才看到a=1,然后准备a = a+1。
但是还没做,另外一个线程就先将a变成2了。导致了上一个线程计算后本来应该是a = 1 + 1,变成了a = 2 + 1
计算结果就不对了。

那么对于这种情况当然是不允许发生的,解决方案就是当发现另外一个线程更新了共享变量,就会把cpu缓存中的数据给失效。
然后都重新读取最新的变量值。

这里有一个前提是共享变量,因为两个线程都会用到a,所以a是共享变量。

那么我们在聊伪共享就简单了,下面举一个伪共享变量的例子。

public class ContendedTest {

    volatile long a;
    
    volatile long b;
    
    @Test
    public void test() throws Exception {
        ContendedTest c = new ContendedTest();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 10000_0000L; i++) {
                c.a = i;
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 10000_0000L; i++) {
                c.b = i;
            }
        });
        final long start = System.nanoTime();
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        // 1933
        System.out.println((System.nanoTime() - start) / 100_0000);
    }
     
}    

两个线程分别来更新a和b属性,根据缓存失效的原理,因为a和b都在同一个对象中,当一个属性被更新,就会触发cpu缓存失效。
那么等于这种情况cpu缓存就没什么用了。我们思考下两个线程分别更新a和b,而a和b没有任何关系。那么a和b是共享变量吗?
当然不是,这就叫伪共享。

二、主动告诉程序伪共享

我们可以使用 @Contended 来声明伪共享变量,从而是cpu不更新缓存。
本地测试时候记得加上jvm参数 -XX:-RestrictContended,否则无效哦。

public class ContendedTest {

    @Contended
    volatile int a;

    @Contended
    volatile int b;

    @Test
    public void test() throws Exception {
        ContendedTest c = new ContendedTest();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 10000_0000L; i++) {
                c.a = i;
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 10000_0000L; i++) {
                c.b = i;
            }
        });
        final long start = System.nanoTime();
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println((System.nanoTime() - start) / 100_0000);
    }
}

那么你猜下性能能提高多少呢? 前者1933后者758ms,差不多2.5倍的样子。

那么留下一个问题? 有多少场景都在使用@Contended呢? 知道的请留言评论。

文章来源: springlearn.blog.csdn.net,作者:西魏陶渊明,版权归原作者所有,如需转载,请联系作者。

原文链接:springlearn.blog.csdn.net/article/details/125830513

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。