设计思路
- 资源池数据结构:使用一个
LinkedList
来存储可用资源,int
变量记录资源总数。
- 锁机制:采用
ReentrantLock
作为同步锁,它支持公平与非公平模式,这里选择非公平模式以提高性能,因为公平模式会导致线程频繁切换。
- 条件变量:通过
Condition
实现线程的等待与唤醒机制。当资源池为空时,获取资源的线程进入等待状态;当有资源释放时,唤醒等待的线程。
核心代码实现
import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ResourcePool {
private final LinkedList<Object> resources;
private final int maxResources;
private final ReentrantLock lock = new ReentrantLock(false);
private final Condition available = lock.newCondition();
public ResourcePool(int maxResources) {
this.resources = new LinkedList<>();
this.maxResources = maxResources;
for (int i = 0; i < maxResources; i++) {
resources.add(new Object());
}
}
public Object acquire() throws InterruptedException {
lock.lock();
try {
while (resources.isEmpty()) {
available.await();
}
return resources.removeFirst();
} finally {
lock.unlock();
}
}
public void release(Object resource) {
lock.lock();
try {
if (resources.size() < maxResources) {
resources.addLast(resource);
available.signal();
}
} finally {
lock.unlock();
}
}
}
性能优化措施及原理
- 非公平锁:
ReentrantLock
默认采用非公平模式,它在锁可用时,允许等待队列中的线程和新请求的线程竞争锁,减少线程上下文切换开销。因为公平锁会严格按照线程等待顺序分配锁,可能导致新请求线程需要等待较长时间,增加了不必要的上下文切换。
- 使用
while
循环等待:在acquire
方法中,使用while (resources.isEmpty())
循环等待,而不是if
。这是因为await
方法可能会因为虚假唤醒(spurious wakeup)而返回,使用while
循环可以确保在资源真正可用时才继续执行,避免无效的资源获取尝试,提高性能。
死锁防范措施及原理
- 正确的锁获取与释放顺序:在
acquire
和release
方法中,都严格遵循先获取锁,操作完成后再释放锁的顺序。避免在持有锁的情况下调用可能会阻塞的方法(如await
)而不释放锁,从而防止死锁。
- 单一锁控制:整个资源池使用一个
ReentrantLock
进行同步控制,避免了多个锁交叉使用可能导致的死锁情况。如果使用多个锁,不同线程可能以不同顺序获取锁,容易形成死锁环。