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

周末去前公司讲解了一段很久以前的代码(当时交接的人早就走了,后来也不知道他们什么情况),顺便帮另一位同事解决数据存取的问题。
需求是这样的:安卓录制音视频,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();
				}
			}
		}
	}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值