前言
环形缓冲器(ringr buffer),也称作圆形队列(circular queue),循环缓冲区(cyclic buffer),圆形缓冲区(circula buffer),是一种用于表示一个固定尺寸、头尾相连的缓冲区的数据结构,适合缓存数据流。
描述
圆形缓冲区的一个有用特性是:当一个数据元素被用掉后,其余数据元素不需要移动其存储位置。相反,一个非圆形缓冲区(例如一个普通的队列)在用掉一个数据元素后,其余数据元素需要向前搬移。换句话说,圆形缓冲区适合实现先进先出缓冲区,而非圆形缓冲区适合后进先出缓冲区。
圆形缓冲区适合于事先明确了缓冲区的最大容量的情形。扩展一个圆形缓冲区的容量,需要搬移其中的数据。因此一个缓冲区如果需要经常调整其容量,用链表实现更为合适。
两种结构
- 圆形缓冲区
- 链表型缓冲区,这个例子理论,第一个元素之前是A,写到第六个元素时,已经到达尾部,需要掉头从第一个元素开始写。
链表实现
下面是我实现的一个简单的无锁链表型缓冲区
/**
* 无锁链表型缓冲区,后进先出
*
* @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