算法练习(45): 环形缓冲区(1.3.39)

作者: kyson老师 | 来源:发表于2017-11-17 23:59 被阅读210次

    本系列博客习题来自《算法(第四版)》,算是本人的读书笔记,如果有人在读这本书的,欢迎大家多多交流。为了方便讨论,本人新建了一个微信群(算法交流),想要加入的,请添加我的微信号:zhujinhui207407 谢谢。另外,本人的个人博客 http://www.kyson.cn 也在不停的更新中,欢迎一起讨论

    算法(第4版)

    知识点

    • 环形缓冲区

    题目

    1.3.39 环形缓冲区。环形缓冲区,又称为环形队列,是一种定长为 N 的先进先出的数据结构。它在进程间的异步数据传输或记录日志文件时十分有用。当缓冲区为空时,消费者会在数据存入缓冲区前等待;当缓冲区满时,生产者会等待数据存入缓冲区。为 RingBuffer 设计一份 API 并用(回环)数组将其实现。


    1.3.39 Ring buffer. A ring buffer, or circular queue, is a FIFO data structure of a fixed size N. It is useful for transferring data between asynchronous processes or for storing log files. When the buffer is empty, the consumer waits until data is deposited; when the buffer is full, the producer waits to deposit data. Develop an API for a RingBuffer and an implementation that uses an array representation (with circular wrap-around).

    分析

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

    生产者消费者问题
    (英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。 要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。通常采用进程间通信的方法解决该问题,常用的方法有信号灯法[1]等。如果解决方法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。该问题也能被推广到多个生产者和消费者的情形。

    回到这道题,网上的解法有很多,因为这道题可以做的很简单,也可以做的很复杂。这篇文章Java Ring Buffer大家可以借鉴一下,现在我来给出几个答案。

    答案

    API

    返回类型 函数 备注
    boolean reset() 重新设置
    int size() 大小
    boolean put(Item x) 存入缓存
    Item take() 读取
    public class RingBuffer<Item> {
    
        public Item[] a = null;
    
        public int writePos = 0;
        public int readPos = 0;
        public boolean flipped = false; // the flip marker
    
        public RingBuffer(int cap) {
            this.a = (Item[]) new Object[cap];
        }
    
        public void reset() {
            this.writePos = 0;
            this.readPos = 0;
            this.flipped = false;
        }
    
        public int size() {
            return a.length;
        }
    
        public int available() {
            if (!flipped) {
                return writePos - readPos;
            }
            return size() - readPos + writePos;
        }
    
        public int remainingCapacity() {
            if (!flipped) {
                return size() - writePos;
            }
            return readPos - writePos;
        }
    
        public boolean put(Item x) {
            if (!flipped) {
                if (writePos == size()) {
                    writePos = 0;
                    flipped = true;
    
                    if (writePos < readPos) {
                        a[writePos++] = x;
                        return true;
                    } else {
                        return false;
                    }
                } else {
                    a[writePos++] = x;
                    return true;
                }
            } else {
                if (writePos < readPos) {
                    a[writePos++] = x;
                    return true;
                } else {
                    return false;
                }
            }
        }
    
        public Item take() {
            if (!flipped) {
                if (readPos < writePos) {
                    return a[readPos++];
                } else {
                    return null;
                }
            } else {
                if (readPos == size()) {
                    readPos = 0;
                    flipped = false;
    
                    if (readPos < writePos) {
                        return a[readPos++];
                    } else {
                        return null;
                    }
                } else {
                    return a[readPos++];
                }
            }
        }
    }
    

    这个方案中有个标记为 flipped,用于标记是否已经被读取过一次。

    测试用例

    public static void main(String[] args){
        int capacity = 10;
        RingBuffer<String> ringBuffer = new RingBuffer<String>(capacity);
        
        /*******************测试用例1*************************/
        for (int i = 0; i < capacity; i++) {
            String inputItem = i+"";
            boolean putSuccess = ringBuffer.put(inputItem);
            System.out.println(putSuccess ? "插入" + inputItem + "成功" : "插入" + inputItem + "失败" );
        }
        
        /*******************测试用例2*************************/
        for (int i = 0; i < capacity + 1; i++) {
            
            if (i == capacity - 1) {
                String takeItem = ringBuffer.take();
                System.out.println("取出" + takeItem + "成功");
            }
            
            if (i == capacity) {
                String takeItem = ringBuffer.take();
                System.out.println("取出" + takeItem + "成功");
            }
            
            String inputItem = i+"";
            boolean putSuccess = ringBuffer.put(inputItem);
            System.out.println(putSuccess ? "插入" + inputItem + "成功" : "插入" + inputItem + "失败" );
        }
        
    }
    

    代码索引

    RingBuffer.java

    广告

    我的首款个人开发的APP壁纸宝贝上线了,欢迎大家下载。

    相关文章

      网友评论

        本文标题:算法练习(45): 环形缓冲区(1.3.39)

        本文链接:https://www.haomeiwen.com/subject/qhisvxtx.html