BufferPool
是一个用于管理字节缓冲区(ByteBuffer)的内存池实现,主要目的是减少频繁的内存分配和释放操作,提高内存使用效率。
主要成员变量
-
totalSize
: 内存池总大小 -
slotSize
: 标准槽位大小 -
free
: 当前剩余可用内存大小 -
slotQueue
: 存放可重用的固定大小(slotSize)的 ByteBuffer 队列 -
waiters
: 等待内存分配的线程条件队列 -
lock
: 用于同步控制的锁
核心方法
1. 构造函数 BufferPool(int totalSize, int slotSize)
初始化内存池的总大小和标准槽位大小,并设置初始可用内存为总大小。
2. allocate(int size, long timeout)
方法
申请指定大小的内存缓冲区:
-
首先检查申请大小是否合法
-
如果申请大小正好是 slotSize 且 slotQueue 中有可用缓冲区,直接返回
-
如果当前可用内存(包括可释放的固定缓冲区)足够,则分配新内存
-
如果内存不足,当前线程进入等待队列,超时或被唤醒后重试
-
在 finally 块中确保如果有等待线程且内存可用时唤醒下一个等待线程
3. deallocate(ByteBuffer byteBuffer)
方法
释放缓冲区:
-
如果缓冲区大小等于 slotSize,清空并放回 slotQueue 供重用
-
否则,直接增加可用内存大小
-
如果有等待线程,唤醒一个
4. freeUp(int size)
私有方法
当需要分配内存但当前空闲内存不足时,尝试从 slotQueue 中释放缓冲区来腾出空间。
设计特点
-
两种内存管理策略:
-
对于 slotSize 大小的缓冲区:重用机制
-
其他大小的缓冲区:直接分配/释放
-
-
公平等待:
- 使用队列管理等待线程,避免饥饿
-
超时控制:
- 支持带超时的内存申请
-
线程安全:
- 使用 ReentrantLock 和 Condition 保证线程安全
java
package com.zsy;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 字节缓冲区内存池实现类
*
* <p>本类实现了一个线程安全的字节缓冲区内存池,支持固定大小的槽位分配和释放,
* 并提供带超时机制的缓冲区申请功能。</p>
*
* @author [email protected]
*/
public class BufferPool {
/**
* 内存池总容量(字节)
*/
private final int totalSize;
/**
* 每个槽位的大小(字节)
*/
private final int slotSize;
/**
* 当前空闲内存大小(字节)
*/
private int free;
/**
* 可用槽位队列(先进先出)
*/
private final Deque<ByteBuffer> slotQueue = new ArrayDeque<>();
/**
* 等待队列(存储等待内存分配的线程条件)
*/
private final Deque<Condition> waiters = new ArrayDeque<>();
/**
* 可重入锁,用于线程同步
*/
private final Lock lock = new ReentrantLock();
/**
* 构造函数
*
* @param totalSize 内存池总容量(字节)
* @param slotSize 每个槽位的大小(字节)
* @throws IllegalArgumentException 如果参数不合法(小于等于0)
*/
public BufferPool(int totalSize, int slotSize) {
if (totalSize <= 0 || slotSize <= 0) {
throw new IllegalArgumentException("内存池大小和槽位大小必须大于0");
}
this.totalSize = totalSize;
this.slotSize = slotSize;
this.free = totalSize;
}
/**
* 申请指定大小的字节缓冲区
*
* @param size 请求的缓冲区大小(字节)
* @param timeout 最大等待时间(毫秒)
* @return 分配到的ByteBuffer对象
* @throws InterruptedException 如果线程在等待时被中断
* @throws RuntimeException 如果请求大小不合法或超时未分配到内存
*/
public ByteBuffer allocate(int size, long timeout) throws InterruptedException {
// 方法实现...
if (size > totalSize || size <= 0) {
throw new RuntimeException("你的申请容量异常" + size);
}
lock.lock();
try {
if (size == slotSize && !slotQueue.isEmpty()) {
return slotQueue.pollFirst();
}
if ((free + slotQueue.size() * slotSize) >= size) {
freeUp(size);
free -= size;
return ByteBuffer.allocate(size);
}
Condition condition = lock.newCondition();
waiters.addLast(condition);
long remainTime = timeout;
try {
while (true) {
long start = System.currentTimeMillis();
boolean wakeup = condition.await(remainTime, TimeUnit.MILLISECONDS);
if (!wakeup) {
throw new RuntimeException("规定时间内不能申请需要的内存");
}
if (size == slotSize && !slotQueue.isEmpty()) {
return slotQueue.pollFirst();
}
if ((free + slotQueue.size() * slotSize) >= size) {
freeUp(size);
free -= size;
return ByteBuffer.allocate(size);
}
remainTime -= System.currentTimeMillis() - start;
}
} finally {
waiters.remove(condition);
}
} finally {
if (!waiters.isEmpty() && !(free == 0 && slotQueue.isEmpty())) {
waiters.peekFirst().signal();
}
lock.unlock();
}
}
/**
* 释放字节缓冲区到内存池
*
* @param byteBuffer 要释放的ByteBuffer对象
* @throws NullPointerException 如果参数为null
*/
public void deallocate(ByteBuffer byteBuffer) {
// 方法实现...
lock.lock();
try {
if (byteBuffer.capacity() == this.slotSize) {
byteBuffer.clear();
this.slotQueue.addLast(byteBuffer);
} else {
free += byteBuffer.capacity();
}
if (!waiters.isEmpty()) {
waiters.peekFirst().signal();
}
} finally {
lock.unlock();
}
}
/**
* 内部方法:释放足够的内存以满足请求大小
*
* @param size 需要的内存大小
*/
private void freeUp(int size) {
// 方法实现...
while (free < size && !slotQueue.isEmpty()) {
free += slotQueue.pollFirst().capacity();
}
}
}