您的位置:首页 > Web前端

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

2017-09-25 01:29 393 查看
  周末去前公司讲解了一段很久以前的代码(当时交接的人早就走了,后来也不知道他们什么情况),顺便帮另一位同事解决数据存取的问题。

  需求是这样的:安卓录制音视频,C调用Java方法传递一段不定长度的
short[]
类型数据,要求是按照每段2048字节格式传递给另一个API。最好的方式是实现存储字节的队列,但简单利用ByteBuffer来操作一下也是相当便捷的。

  把ByteBuffer当作一个货物的中转点,遵循先进先出的规则将所有货物依次有序的堆放在中转点,以便下次转出。转入和转出的工作是由两个不同的车队去完成,考虑到这一点,应该使用异步的方式来存取这批货物。

简述

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

  

常量定义

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

// 限制ByteBuffer最大容量
final static int BUFFER_SIZE = 8888;
// 定义全局ByteBuffer
final static ByteBuffer DATA_BUFFER = ByteBuffer.allocate(BUFFER_SIZE);
// 定义每次将要存到ByteBuffer中的数据长度 1000~3000
final 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();
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java ByteBuffer