MST

星途 面试题库

面试题:Java多线程条件变量与性能优化及死锁防范

在高并发的Java多线程应用中,使用条件变量(Condition)实现一个资源池。资源池中有固定数量的资源,多个线程竞争获取和释放资源。要求考虑性能优化,比如如何减少线程上下文切换,同时要避免死锁的发生。请详细阐述设计思路,并给出核心代码实现,且说明你采取的性能优化和死锁防范措施的原理。
43.2万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 资源池数据结构:使用一个LinkedList来存储可用资源,int变量记录资源总数。
  2. 锁机制:采用ReentrantLock作为同步锁,它支持公平与非公平模式,这里选择非公平模式以提高性能,因为公平模式会导致线程频繁切换。
  3. 条件变量:通过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();
        }
    }
}

性能优化措施及原理

  1. 非公平锁ReentrantLock默认采用非公平模式,它在锁可用时,允许等待队列中的线程和新请求的线程竞争锁,减少线程上下文切换开销。因为公平锁会严格按照线程等待顺序分配锁,可能导致新请求线程需要等待较长时间,增加了不必要的上下文切换。
  2. 使用while循环等待:在acquire方法中,使用while (resources.isEmpty())循环等待,而不是if。这是因为await方法可能会因为虚假唤醒(spurious wakeup)而返回,使用while循环可以确保在资源真正可用时才继续执行,避免无效的资源获取尝试,提高性能。

死锁防范措施及原理

  1. 正确的锁获取与释放顺序:在acquirerelease方法中,都严格遵循先获取锁,操作完成后再释放锁的顺序。避免在持有锁的情况下调用可能会阻塞的方法(如await)而不释放锁,从而防止死锁。
  2. 单一锁控制:整个资源池使用一个ReentrantLock进行同步控制,避免了多个锁交叉使用可能导致的死锁情况。如果使用多个锁,不同线程可能以不同顺序获取锁,容易形成死锁环。