`

基于锁的并发算法 vs 无锁的并发算法

阅读更多

文本通过实例比较了各种基于锁的并发算法和无锁并发算法的性能:系 http://mechanical-sympathy.blogspot.com/2013/08/lock-based-vs-lock-free-concurrent.html  文翻译

 

      上周在由Heinz Kabutz通过JCrete 组织的开放空间会议(unconference)上,我参加一个新的java规范 JSR166 StampedLock的审查会议。StampedLock 是为了解决多个readers 并发访问共享状态时,系统出现的地址竞争问题。在设计上通过使用乐观的读操作,StampedLock 比ReentrantReadWriteLock 更加高效;

 

      在会议期间,我突然意思到两点:第一、我想是时候该去回顾java中锁的实现的现状;第二、虽然StampedLock 看上去是JDK很好的补充,但是它视乎忽略了一个事实,即在多个reader的场景里,无锁的算法通常是更好的解决方案。

 测试:

      为了比较不同的实现方式,我需要采用一种不偏向任意一方的API测试用例。 比如:API必须不产生垃圾、并且允许方法是原子性的。一个简单的测试用例是设计一个可在两维空间中移动其位置的太空船,它位置的坐标可以原子性的读取;每一次事物里至少需要读写2个域,这使得并发变得非常有趣;

  

/**
 * 并发接口,表示太空船可以在2维的空间中移动位置;并且同时更新读取位置
 */
public interface Spaceship
{
    /**
     *  读取太空船的位置到参数数组 coordinates 中
     *
     * @param coordinates 保存读取到的XY坐标.
     * @return 当前的状态
     */
    int readPosition(final int[] coordinates);
 
    /**
     *  通过增加XY的值表示移动太空船的位置。
     *
     * @param xDelta x坐标轴上移动的增量.
     * @param yDelta y坐标轴上移动的增量.
     * @return the number of attempts made to write the new coordinates.
     */
    int move(final int xDelta, final int yDelta);
}

 

      上面的API通过分解一个不变的位置对象,本身是干净的 。但是我想保证它不产生垃圾,并且需要能最直接的更新多个内容域。这个API可以很容易地扩展到三维空间,并实现原子性要求。

 

      为每一个飞船都设置多个实现,并且作为一个测试套件。本文中所有的代码和结果都可以在这里找到。

 

      该测试套件会依次运行每一种实现.并且使用 megamorphic dispatch模式,防止并发访问中的方法内联(inlining),锁粗化(lock-coarsening),循环展开( loop unrolling)的问题;

 

       每种实现都执行下面4个不同的线程的情况,结果也是不同的;

      1 reader - 1 writer

      2 readers - 1 writer

      3 readers - 1 writer

      2 readers - 2 writers

 

      所有的测试运行在64位机器、Java版本:1.7.0_25、 Linux版本:3.6.30、4核 2.2GHz Ivy Bridge (第三代Core i系列处理器)i7-3632QM的环境上。

 

      测试吞吐量的时候,是通过每一种实现都重复测试超过5次,每一次都运行5秒以上,以保证系统足够预热,下面的结果都是第5次之后平均每秒吞吐量。为了更像一个典型的java应用;没有采用会导致明显减少差异的线程依附性(thread affinity)和多核隔离(core isolation )技术;

结果:



 

 

 

 

      上述图表的原始数据可以在这里找到

分析:

      结果里面真正令我吃惊的是ReentrantReadWriteLock的性能,我没有想到的是,在这样的场景下它在读和少量写之间取得的巨大的平衡性,

 

      我主要的收获:

      1、StampedLock 对现存的锁实现有巨大的改进,特别是在读线程越来越多的场景下:

      2、StampedLock有一个复杂的API,对于加锁操作,很容易误用其他方法;

      3、当只有2个竞争者的时候,Synchronised是一个很好的通用的锁实现;

      4、当线程增长能够预估,ReentrantLock是一个很好的通用的锁实现;

      5、选择使用ReentrantReadWriteLock时,必须经过小心的适度的测试 ;所有重大的决定,必须在基于测试数据的基础上做决定;

      6、无锁的实现比基于锁的算法有更好短吞吐量;

 

结论:

      非常开心能看到无锁技术对基于锁的算法的影响; 乐观锁的策略,实际上就是一个无锁算法技术。

      以我的经验看,教学和开发中的无锁算法,不仅能显著改善吞吐量;同时他们也提供更低的延迟。

  • 大小: 9.9 KB
  • 大小: 10.4 KB
  • 大小: 9.5 KB
  • 大小: 9.8 KB
0
1
分享到:
评论

相关推荐

    多线程并发访问无锁队列的算法研究.pdf

    这种算法基于Compare-and-swap (CAS)操作,在共享内存的多核处理器上表现出远超基于锁的传统算法的性能,并已被Java并发包所采纳。 #### 2. MS-Queue算法及改进算法 ##### 2.1 MS-Queue算法 MS-Queue算法是1996年...

    一个无锁算法的库实现

    无锁算法,也被称为锁-free或原子操作算法,是一种并发编程技术,旨在提高多线程环境中的性能。在传统的同步机制中,如互斥锁,线程必须竞争资源,这可能导致上下文切换和死锁等问题。无锁算法通过避免锁的使用来...

    如何实现超高并发的无锁缓存

    ### 如何实现超高并发的无锁缓存 #### 一、需求缘起 在某些特定的业务场景下,系统面临着写多读少的情况,即大量的请求是对数据进行修改,而较少的请求则用于数据的读取。例如,在滴滴打车应用中,司机的位置信息...

    基于机器学习算法的原发性高血压并发冠心病的患病风险研究.pdf

    "基于机器学习算法的原发性高血压并发冠心病的患病风险研究" 本研究旨在建立一个基于机器学习算法的原发性高血压并发冠心病患病风险模型,以筛选出相关的危险因素,并提供一种计算机辅助诊断方法。研究人员收集了...

    基于无锁数据结构的FIFO队列算法.pdf

    这篇文章介绍了一种基于无锁数据结构的FIFO(先进先出)队列算法,该算法被设计为一种高效的核间通信机制。 无锁数据结构的关键在于,它通过原子操作或者特定的算法设计,确保数据结构在并发读写时仍然保持正确性和...

    CAS无锁算法.pdf

    CAS无锁算法是一种在多线程环境下用于实现同步的技术,它与传统的锁机制相比,有其独特的优点和应用场景。CAS是英文“Compare and Swap”的缩写,中文含义为“比较并交换”。该算法通过比较变量的预期值与当前值,若...

    基于cas的无锁队列实现

    综上所述,基于CAS的无锁队列在高并发环境中能提供优秀的性能,通过避免锁的使用减少了线程间的阻塞,提高了系统的吞吐量。然而,无锁编程也增加了实现的复杂性,需要对并发原理有深入理解。在实际应用中,开发者...

    基于令牌桶算法实现的SpringBoot无锁限流插件

    总结来说,基于令牌桶算法的SpringBoot无锁限流插件为Java Web开发提供了一种高效且灵活的流量控制解决方案。通过方法和系统级别的限流策略,以及快速失败和CAS阻塞模式,它能够帮助我们构建更健壮、响应更快的应用...

    Java并发——无锁实现

    在Java并发编程中,无锁实现是一个高级技术,它可以让多个线程在没有使用传统锁机制(如synchronized关键字或显示锁Lock)的情况下,安全地执行对共享资源的操作。无锁机制主要依赖于硬件的原子指令,尤其是比较并...

    网络游戏-基于并发无锁环形队列的网络速率实时统计方法.zip

    本文档“基于并发无锁环形队列的网络速率实时统计方法”探讨了一种高效、低延迟的技术手段,即使用并发无锁环形队列来实现这一目标。这种技术尤其适用于多线程环境,能够确保数据的安全性和一致性,同时减少锁的开销...

    基于无锁方法的二叉搜索树算法之计算机研究.docx

    本文研究了一种基于无锁方法的二叉搜索树算法,旨在解决多线程并发环境下的数据访问冲突问题。该算法可以应用于多核处理器环境,提高程序的运行效率和可扩展性。 1.1 无锁方法的必要性 在多核处理器环境下,高并发...

    并发无锁奥秘相关代码以及执行程序

    无锁编程基于原子操作(Atomic Operations),这些操作在单个指令中完成,不会被中断。在C++中,可以使用`std::atomic`库来实现原子操作。通过避免锁的使用,无锁编程能够减少上下文切换,提高并行执行的效率,同时...

    包括并发的基础理论知识、不同并发模型的选择与适用环境、编写并发程序的基本步骤,并发算法的正确性证明与性能评价,以及在编写并发程序时遵循的一些指导原则等

    并发算法的正确性证明是确保并发程序行为正确性的关键。这通常涉及到状态机建模、Lamport逻辑时钟、Dijkstra的信号量、Petri网等方法。性能评价则涉及吞吐量、响应时间、资源利用率等指标,通过模拟和基准测试来评估...

    快速非阻塞并发队列算法

    本文提出了一种新的非阻塞并发队列算法以及一种两锁队列算法,这两种算法都允许一个入队操作和一个出队操作同时进行。这些算法简单、快速且实用,在先前的研究文献中并未发现它们的存在。在对12节点SGI Challenge多...

    基于软件互斥算法的临界区进程互斥的模拟实现

    Lamport算法,也称为自旋锁,是基于信号量机制的简化版本。它利用一个特殊类型的信号量,每个进程在尝试进入临界区前先尝试获取信号量,若无法获取则进入自旋状态,等待其他进程释放信号量。 Peterson算法是为两个...

    Linux内核中的无锁队列 - kfifo

    - **lock**: 当无法确保任何时候只有一个读线程和写线程时,需要使用此锁来保护数据结构免受并发修改的影响。 #### 功能特性 `kfifo`提供以下功能特性: 1. **只支持一个读者和一个写者并发操作**:这意味着在多...

    基于分布式B树编译的高效并发访问控制算法.pdf

    为解决这一问题,黄正鹏在其论文中提出了一种新的基于分布式B树编译的高效并发访问控制算法。 分布式B树是一种数据结构,它在分布式系统中被用于管理和存储数据,以实现高效率的数据访问和检索。其基本原理是将数据...

    基于机器学习算法的脑出血相关肺炎预测模型研究.pdf

    【基于机器学习算法的脑出血相关肺炎预测模型研究】 这篇研究论文主要探讨了利用机器学习算法来预测脑出血患者并发肺炎的可能性。脑出血是一种严重的神经系统疾病,常常伴有并发症,如肺炎,这会增加患者的死亡风险...

    Java 高并发四:无锁详细介绍

    无锁类通过避免使用传统的互斥锁,使得多个线程可以在不阻塞彼此的情况下并发修改共享数据,从而减少了线程上下文切换的开销。在Java中,无锁实现主要依赖于硬件层面的原子操作,如CAS(Compare and Swap)。 1. ...

    基于C++的银行家算法模拟实现

    本实验是基于Dijkstra的银行家算法的实现,该算法可用于在操作系统中避免死锁。 该算法的基本思想是:让用户输入进程数与资源类数,并输入每个进程对每类资源的最大需求量,已占用数,以及系统中当前每类资源的可用...

Global site tag (gtag.js) - Google Analytics