一种极致性能的缓冲队列

本文介绍了一种适用于多生产者、单消费者模型的高性能缓冲队列实现,采用环形队列结构和AtomicInteger确保线程安全。通过对比分析,展示了优化后的RingBuffer性能提升,包括批量插入、缓存行填充等改进,并探讨了进一步的优化策略。该实现已被Apache SkyWalking采纳。
摘要由CSDN通过智能技术生成

本文已收录 https://github.com/lkxiaolou/lkxiaolou 欢迎star。

背景

在多线程下的生产者-消费者模型中,需求满足如下情况:

对生产者生产投递数据的性能要求非常高
多个生产者,单个(多个也可以,本文只介绍单个的情况)消费者
当消费者跟不上生产者速度时,可容忍少部分数据丢失
生产者是单条单条地生产数据
举个日志采集的例子,日志在不同的线程上生产,在日志生产速度远超消费者速度时,可以丢弃部分数据,要求打日志的性能损耗最小,这种情况下可采用本文提供的极致性能的缓冲队列。

实现细节

多个生产者向一个缓冲队列提交消息,说到底是线程安全问题,如果不考虑线程安全,性能必然是最高的,但出现的问题是,数据经常被覆盖。虽然可以容忍少部分数据丢失,但也是在消费者跟不上生产者速度时。缓冲区必然有界,无界可能导致内存泄露,如果缓冲区满,再生产新数据,可选的策略一般有如下几种:

  • 阻塞直到被消费
  • 覆盖旧数据
  • 丢弃新数据
    在要求对生产者性能损耗最小的情况下一般不选1,通常采取覆盖策略。

环形队列

有一种环形队列的数据结构(ring buffer)可以很好的解决解决上面提到的生产者-消费者模型、缓冲区有界、覆盖策略。通常用数组来实现ring buffer,只要保证生产者获取下标是线程安全的即可解决线程安全问题。而且数组内存预先分配加上连续内存索引更加快速的特点也保证了强悍的性能。

AtomicInteger

在环形队列上如何保证线程安全地获取数组下标?线程安全地自增我们想到了AtomicInteger,很容易写出如下代码

public class AtomicRangeInteger extends Number {
   

    private final AtomicInteger value;

    private final int startValue;
    private final int endValue;

    public AtomicRangeInteger(int startValue, int endValue) {
   
        this.startValue = startValue;
        this.endValue = endValue;
        this.value = new AtomicInteger(startValue);
    }

    public final int incrementAndGet() {
   
        int next;
        do {
   
            next = value.incrementAndGet();
            if (next > endValue && value.compareAndSet(next
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值