记录一个ByteBuffer在多线程下存取的简单应用

来源:互联网 发布:求职简历邮件正文知乎 编辑:程序博客网 时间:2024/06/06 05:30

  周末去前公司讲解了一段很久以前的代码(当时交接的人早就走了,后来也不知道他们什么情况),顺便帮另一位同事解决数据存取的问题。
  需求是这样的:安卓录制音视频,C调用Java方法传递一段不定长度的short[]类型数据,要求是按照每段2048字节格式传递给另一个API。最好的方式是实现存储字节的队列,但简单利用ByteBuffer来操作一下也是相当便捷的。
  把ByteBuffer当作一个货物的中转点,遵循先进先出的规则将所有货物依次有序的堆放在中转点,以便下次转出。转入和转出的工作是由两个不同的车队去完成,考虑到这一点,应该使用异步的方式来存取这批货物。

简述

  这段代码主要利用ByteBuffer的compact()方法,将数据前移,保证ByteBuffer有足够的空间存新数据的同时,也能够将旧的数据从ByteBuffer中逐端取出,实现先进先出的队列效果。
  

常量定义

  定义基本规则常量,为调试直观,常量取值范围都比较小:

// 限制ByteBuffer最大容量final static int BUFFER_SIZE = 8888;// 定义全局ByteBufferfinal static ByteBuffer DATA_BUFFER = ByteBuffer.allocate(BUFFER_SIZE);// 定义每次将要存到ByteBuffer中的数据长度 1000~3000final static int WRITE_LEN = 000;// 定义每次从ByteBuffer读取的数据长度final static int READ_LEN = 2048;// 同步锁final static Lock LOCK = new ReentrantLock(true);// 表示数据读取是否结束static boolean readEnd = false;

再定义数据读取结束的标识:

// 表示数据读取是否结束static boolean readEnd = false;

main方法

  将存取作为两个线程去执行,通过ByteBuffer(中转站)的方式,将一个文件(货物)copy为另一个文件(转运终点):

public static void main(String[] args) throws Exception {    File readFile = new File("");    File writeFile = new File("");    new PutBufferThread(readFile).start();    new GetBufferThread(writeFile).start();}

三个重要属性:

*  position在本段代码中,始终表现为有效数据长度;*
*  limit始终为ByteBuffer最大容量,也就是上限;*
*  remaining则表示为ByteBuffer的空余容量。*

  PutBufferThread,将数据存到ByteBuffer中

  PutBufferThread,表现为货物转入车队,货物转入到中转站前,会先询问中转站是否还有足够的空间用来存放本次运输的货物,空间不足或者转出车队正在装货,就稍作等待。

public class PutBufferThread extends Thread {    private InputStream in;    PutBufferThread(File file) {        try {            this.in = new FileInputStream(file);        } catch (FileNotFoundException e) {            e.printStackTrace();        }    }    @Override    public void run() {        try {            byte[] b = new byte[WRITE_LEN];            while (true) {                LOCK.lock(); // 锁住 然后检查一下remaining                // 检查ByteBuffer剩余容量是否能存放一次指定长度的数据                if (DATA_BUFFER.remaining() >= WRITE_LEN) {                    // 从文件中读出一段数据                    if (in.read(b, 0, b.length) == -1) {                        LOCK.unlock();// 如果文件读取结束,直接解锁并结束循环                        break;                    }                    // put进ByteBuffer                    DATA_BUFFER.put(b, 0, len);                }                LOCK.unlock(); // 解锁            }            readEnd = true; // 文件读取结束            in.close(); // 关闭文件流        } catch (IOException e) {            e.printStackTrace();        }    }}

GetBufferThread,从ByteBuffer中取出数据

  GetBufferThread表现为转出车队,转出前询问是否有足量的货物运输,这里定义为2048。货物不足或转出车队正在卸货时稍作等待。

public class GetBufferThread extends Thread {    private OutputStream out;    public GetBufferThread(File file) {        try {            this.out = new FileOutputStream(file);        } catch (FileNotFoundException e) {            e.printStackTrace();        }    }    @Override    public void run() {        byte[] data = null;        int length = 0;        while (true) {            // 获取有效数据长度            length = DATA_BUFFER.position();            if (length > 0) {                // 如果长度不足2048,且还没有读取结束时,立即开始下一次循环。                if (length < READ_LEN && !readEnd) {                    continue;                }                // 如果数据长度超出2048,则只取2048部分                if (length > READ_LEN) {                    length = READ_LEN;                }                // 读取数据前,锁定                LOCK.lock();                data = new byte[length];                // 将ByteBuffer状态设置为准备读取数据。                DATA_BUFFER.flip();                DATA_BUFFER.get(data); // 取出数据                // 将ByteBuffer中的剩余数据前移,“删除”已取出的部分                DATA_BUFFER.compact();                LOCK.unlock(); // 解锁            } else if (readEnd) {                //如果文件读取结束,且ByteBuffer中没有数据,挑出循环                break;            }            if (length > 0) {                // 将数据存到文件中                try {                    out.write(data, 0, length);                    out.flush();                } catch (IOException e) {                    e.printStackTrace();                }            }        }        DATA_BUFFER.clear(); // 清空缓冲区        if (out != null) {            try {                out.close(); // 关闭流            } catch (IOException e) {                e.printStackTrace();            }        }    }}
原创粉丝点击