水煮Redisson(七)-环形缓冲器

258 阅读2分钟

前言

环形缓冲器(ringr buffer),也称作圆形队列(circular queue),循环缓冲区(cyclic buffer),圆形缓冲区(circula buffer),是一种用于表示一个固定尺寸、头尾相连的缓冲区的数据结构,适合缓存数据流。

描述

圆形缓冲区的一个有用特性是:当一个数据元素被用掉后,其余数据元素不需要移动其存储位置。相反,一个非圆形缓冲区(例如一个普通的队列)在用掉一个数据元素后,其余数据元素需要向前搬移。换句话说,圆形缓冲区适合实现先进先出缓冲区,而非圆形缓冲区适合后进先出缓冲区。
圆形缓冲区适合于事先明确了缓冲区的最大容量的情形。扩展一个圆形缓冲区的容量,需要搬移其中的数据。因此一个缓冲区如果需要经常调整其容量,用链表实现更为合适。

两种结构

  • 圆形缓冲区
    image.png
  • 链表型缓冲区,这个例子理论,第一个元素之前是A,写到第六个元素时,已经到达尾部,需要掉头从第一个元素开始写。
    image.png

链表实现

下面是我实现的一个简单的无锁链表型缓冲区

 /**
 * 无锁链表型缓冲区,后进先出
 *
 * @param <T>
 */
public class RingBuffer<T> {
    private final int capacity;
    private final LinkedList<T> array;
    /**
     * 统计添加元素的次数,用于定位元素的位置
     */
    private final AtomicInteger count = new AtomicInteger(0);
    /**
     * 写索引,用于实现后进先出功能
     */
    private static int writeIndex = 0;

    public RingBuffer(int capacity) {
        if (capacity <= 0) {
            throw new RuntimeException("invalid capacity[" + capacity + "],must elder zero.");
        }
        this.capacity = capacity;
        this.array = new LinkedList<T>();
        for (int i = 0; i < capacity; i++) {
            array.add(null);
        }
    }

    /**
     * 写入数据
     *
     * @param t 数据
     */
    public void offer(T t) {
        // 如果此处用array.size,那么数据满员之后,size永远等于capacity,index恒等于0
        int enumCount = count.getAndIncrement();
        // 写入数据的位置
        int index = enumCount % capacity;
        array.set(index, t);
        log.info("LOCAL_RING,OFFER,index:{},data:{},enumCount:{}",
                index, t, enumCount);
        writeIndex = index;
    }

    /**
     * 获取数据
     *
     * @param limit 需要的数量
     * @return list
     */
    public List<T> poll(int limit) {
        List<T> result;
        int size = array.size();
        if (limit >= size) {
            result = new ArrayList<>(array);
			log.info("LOCAL_RING,POLL ALL,data:{}", result);
            // 清空数据
            for (int i = 0; i < capacity; i++) {
                array.set(i, null);
            }
            return result;
        }
        result = new ArrayList<>();
        for (int i = 0; i < limit; i++) {
            int temp = writeIndex - i;
            if (temp < 0) {
                temp = capacity + temp;
            }
            // 删掉以后,会往前缩进
            T t = array.set(temp, null);
            log.info("LOCAL_RING,POLL,index:{},data:{}", i, t);
            if (t != null) {
                result.add(t);
            }
        }
        count.set(size - limit);
        return result;
    }
}

相应的单元测试

@Test
    public void testLocalRing() {
        RingBuffer<String> ringBuffer = new RingBuffer<String>(5);
        ringBuffer.offer("a");
        ringBuffer.offer("b");
        ringBuffer.offer("c");
        ringBuffer.offer("d");
        ringBuffer.offer("e");
        ringBuffer.offer("f");
        ringBuffer.offer("g");
        List<String> objects = ringBuffer.poll(3);
        log.info("RING,data:{}", Arrays.toString(objects.toArray()));
        ringBuffer.offer("a");
        ringBuffer.offer("b");
        ringBuffer.offer("c");
        ringBuffer.offer("d");
        ringBuffer.offer("e");
        ringBuffer.offer("f");
        ringBuffer.offer("g");
        List<String> objects1 = ringBuffer.poll(6);
        log.info("RING,data:{}", Arrays.toString(objects1.toArray()));
    }

单元测试输出

15:55:27.237 [main] INFO com.essay.RingBufferTest - LOCAL_RING,OFFER,index:0,data:a,enumCount:0
15:55:27.241 [main] INFO com.essay.RingBufferTest - LOCAL_RING,OFFER,index:1,data:b,enumCount:1
15:55:27.241 [main] INFO com.essay.RingBufferTest - LOCAL_RING,OFFER,index:2,data:c,enumCount:2
15:55:27.241 [main] INFO com.essay.RingBufferTest - LOCAL_RING,OFFER,index:3,data:d,enumCount:3
15:55:27.241 [main] INFO com.essay.RingBufferTest - LOCAL_RING,OFFER,index:4,data:e,enumCount:4
15:55:27.241 [main] INFO com.essay.RingBufferTest - LOCAL_RING,OFFER,index:0,data:f,enumCount:5
15:55:27.241 [main] INFO com.essay.RingBufferTest - LOCAL_RING,OFFER,index:1,data:g,enumCount:6
15:55:27.241 [main] INFO com.essay.RingBufferTest - LOCAL_RING,POLL,index:0,data:g
15:55:27.241 [main] INFO com.essay.RingBufferTest - LOCAL_RING,POLL,index:1,data:f
15:55:27.241 [main] INFO com.essay.RingBufferTest - LOCAL_RING,POLL,index:2,data:e
15:55:27.241 [main] INFO com.essay.RingBufferTest - RING,data:[g, f, e]
15:55:27.241 [main] INFO com.essay.RingBufferTest - LOCAL_RING,OFFER,index:2,data:a,enumCount:2
15:55:27.241 [main] INFO com.essay.RingBufferTest - LOCAL_RING,OFFER,index:3,data:b,enumCount:3
15:55:27.241 [main] INFO com.essay.RingBufferTest - LOCAL_RING,OFFER,index:4,data:c,enumCount:4
15:55:27.241 [main] INFO com.essay.RingBufferTest - LOCAL_RING,OFFER,index:0,data:d,enumCount:5
15:55:27.241 [main] INFO com.essay.RingBufferTest - LOCAL_RING,OFFER,index:1,data:e,enumCount:6
15:55:27.241 [main] INFO com.essay.RingBufferTest - LOCAL_RING,OFFER,index:2,data:f,enumCount:7
15:55:27.241 [main] INFO com.essay.RingBufferTest - LOCAL_RING,OFFER,index:3,data:g,enumCount:8
15:55:27.241 [main] INFO com.essay.RingBufferTest - LOCAL_RING,POLL ALL,data:[d, e, f, g, c]
15:55:27.241 [main] INFO com.essay.RingBufferTest - RING,data:[d, e, f, g, c]

Process finished with exit code 0