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

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

一、什么叫伪共享

说到伪共享,就要说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呢? 知道的请留言评论。

相关文章
|
6月前
|
编解码 算法 Java
构建高效的Android应用:内存优化策略详解
随着智能手机在日常生活和工作中的普及,用户对移动应用的性能要求越来越高。特别是对于Android开发者来说,理解并实践内存优化是提升应用程序性能的关键步骤。本文将深入探讨针对Android平台的内存管理机制,并提供一系列实用的内存优化技巧,以帮助开发者减少内存消耗,避免常见的内存泄漏问题,并确保应用的流畅运行。
|
6月前
|
Linux 编译器 C++
C/C++性能优化:从根本上消除拷贝操作的浪费
C/C++性能优化:从根本上消除拷贝操作的浪费
840 1
|
4月前
|
缓存 自然语言处理 Java
浅析JAVA日志中的性能实践与原理解释问题之减少看得见的业务开销问题如何解决
浅析JAVA日志中的性能实践与原理解释问题之减少看得见的业务开销问题如何解决
|
6月前
|
缓存 Java Android开发
构建高效的Android应用:内存优化策略解析
【5月更文挑战第25天】在移动开发领域,性能优化一直是一个不断探讨和精进的课题。特别是对于资源受限的Android设备来说,合理的内存管理直接关系到应用的流畅度和用户体验。本文深入分析了Android内存管理的机制,并提出了几种实用的内存优化技巧。通过代码示例和实践案例,我们旨在帮助开发者识别和解决内存瓶颈,从而提升应用性能。
|
6月前
|
缓存 移动开发 Java
构建高效的Android应用:内存优化策略
【4月更文挑战第16天】 在移动开发领域,尤其是针对资源有限的Android设备,内存优化是提升应用性能和用户体验的关键因素。本文将深入探讨Android应用的内存管理机制,分析常见的内存泄漏问题,并提出一系列实用的内存优化技巧。通过这些策略的实施,开发者可以显著减少应用的内存占用,避免不必要的后台服务,以及提高垃圾回收效率,从而延长设备的电池寿命并确保应用的流畅运行。
|
存储 缓存 Linux
高效利用CPU缓存一致性:优化技巧与策略分析
高效利用CPU缓存一致性:优化技巧与策略分析
|
机器学习/深度学习 缓存 Linux
很底层的性能优化:让CPU更快地执行你的代码
很底层的性能优化:让CPU更快地执行你的代码
|
负载均衡 并行计算 算法
BWA序列比对方法丨针对较大基因组的并行计算和性能优化方式,利用多线程和负载均衡策略提高效率
BWA序列比对方法丨针对较大基因组的并行计算和性能优化方式,利用多线程和负载均衡策略提高效率
|
前端开发 JavaScript Java
移动端性能优化:减少应用的加载时间和内存占用
移动应用的性能对用户体验至关重要。在移动设备上,加载时间和内存占用是两个主要的性能指标。本文将介绍一些有效的技术和策略,帮助开发人员优化移动应用的加载时间并减少内存占用,以提升应用的性能和响应速度。
313 0
|
Web App开发 存储 缓存
前端优化系列 - H5存储及优化
数据存储在性能优化中扮演着极其重要的角色,H5相关的存储非常多,本文详细介绍各种存储的特点和相关优化实践。
3379 0