鉴于您的特定问题,没有一个 java 标准对象可以解决您的所有问题。这是一个我认为是正确的解决方案,并且不会在您的锁定映射中保留任何不必要的键或值:
// we don't use a ConcurrentHashMap, because we have some other operations 
// that need to be performed in atomically with map.put and map.remove.
// ConcurrentHashMap would of course also work, but it doesn't remove the 
// need for external synchronization in in our case.
Map<String, CountingLock> locksMap = new HashMap<String, CountingLock>();
...
HttpResponse myFunction(String key) {
    CountingLock lock;
    synchronized(locksMap){
        lock = locksMap.get(key);
        if(lock == null){
            lock = new CountingLock();
            locksMap.put(key, lock);
        }
        lock.prepare(); // has to be done while holding the lock of locksMap.
                        // basically tells other threads that the current 
                        // thread intends to acquire the lock soon. This way,
                        // the other threads know not to remove this lock 
                        // from locksMap as long as another one has indicated
                        // that he is going to need it soon.
    }
    lock.lock(); // has to be done while NOT holding the lock of locksMap,
                 // or we risk deadlock situations.
    try {
        // ...
        // work
        // ...
    } finally {
        synchronized(locksMap) {
            if(lock.unlock() == 0){
                // no other thread is intending to use this lock any more. 
                // It is safe to remove it from the map. The next thread 
                // will just have to recreate a new lock for the same key.
                locksMap.remove(key);
            }
        }
    }
    return SOMETHING;    
}
private static class CountingLock {
    // The number of threads that are trying to access the protected Key
    private AtomicInteger interestedThreads = new AtomicInteger(0);
    private Lock lock = new ReentrantLock();
    public void prepare(){
        interestedThreads.incrementAndGet();
    }
    public void lock(){
        lock.lock();
    }
    public int unlock(){
        lock.unlock();
        return interestedThreads.decrementAndGet();              
    }
}
此代码在所有情况下都应按预期工作。这是一个有趣的问题要解决:-)