简体   繁体   English

在 ConcurrentHashMap 中使用 Guava 的基于键的信号量 vs 信号量

[英]Key based Semaphores with Guava vs Semaphores in a ConcurrentHashMap

I need a key based Semaphore mechanism in my application and stumbled upon Striped.semaphore(int, int) by Guava.我的应用程序中需要一个基于键的信号量机制,并且偶然发现了Guava 的 Striped.semaphore(int, int) However, it does not behave as expected.但是,它的行为并不像预期的那样。

Using the following code, fetch sometimes returns null.使用以下代码,fetch 有时会返回 null。 Both methods are accessed by different threads.这两种方法都由不同的线程访问。 I expect the thread that calls fetch to wait until a Blubb is available in the map.我希望调用 fetch 的线程等待直到地图中的 Blubb 可用。

private final Striped<Semaphore> semaphores = Striped.semaphore(64, 0);

private final Map<String, Blubb> blubbs = Collections.synchronizedMap(new HashMap<String, Blubb>());

private Semaphore getSemaphore(final String key) {
    return semaphores.get(key);
}

@Override
public void put(String key, Blubb blubb)  {
    blubb.put(key, blubb);
    final Semaphore semaphore = getSemaphore(toUser);
    semaphore.release();
}

@Override
public blubb fetch(final String key) {
    try {
        final Semaphore semaphore = getSemaphore(key);
        final boolean acquired = semaphore.tryAcquire(30, TimeUnit.SECONDS);
        return blubbs.get(key);
    } catch (final InterruptedException e) {
        e.printStackTrace();
    }

    return null;
}

If I switch back to basic Java with the following code everything works as expected.如果我使用以下代码切换回基本 Java,一切都会按预期进行。

private final Map<String, Semaphore> semaphoresMap = new ConcurrentHashMap<String, Semaphore>();

private Semaphore getSemaphore(final String key) {
    Semaphore semaphore = semaphoresMap.get(key);
    if (semaphore == null) {
        semaphore = new Semaphore(0);
        semaphoresMap.put(key, semaphore);
    }
    return semaphore;
}

What am I missing here?我在这里缺少什么? Thanks谢谢

Guava's Striped specifies that multiple keys may potentially map to the same semaphore. Guava 的Striped指定多个键可能映射到同一个信号量。 From the Javadoc:来自 Javadoc:

The guarantee provided by this class is that equal keys lead to the same lock (or semaphore), ie if (key1.equals(key2)) then striped.get(key1) == striped.get(key2) (assuming Object.hashCode() is correctly implemented for the keys).此类提供的保证是相等的键导致相同的锁(或信号量),即 if (key1.equals(key2)) then striped.get(key1) == striped.get(key2)(假设 Object.hashCode () 正确实现了键)。 Note that if key1 is not equal to key2, it is not guaranteed that striped.get(key1) != striped.get(key2);注意如果key1不等于key2,不保证striped.get(key1) != striped.get(key2); the elements might nevertheless be mapped to the same lock.尽管如此,元素可能会映射到同一个锁。 The lower the number of stripes, the higher the probability of this happening.条纹数量越少,发生这种情况的概率就越高。

The underlying assumption in your code seems to be that if the semaphore associated with a particular object has a permit, then that object has an entry in the map, but that's not the case -- if there is an entry in the map for another object that happens to be associated with the same Semaphore , then that permit might be taken by a fetch on a completely different object, which does not actually have an entry in the map.您代码中的基本假设似乎是,如果与特定对象关联的信号量具有许可,则该对象在映射中具有条目,但事实并非如此——如果映射中存在另一个对象的条目恰好与相同的Semaphore相关联,那么该许可可能会被一个完全不同的对象的fetch ,该对象实际上在映射中没有条目。

The 'basic java' example has a potential race condition, computeIfAbsent is an atomic operation and solves this: 'basic java' 示例有一个潜在的竞争条件,computeIfAbsent 是一个原子操作并解决了这个问题:

private final Map<String, Semaphore> semaphoresMap = new ConcurrentHashMap<String, Semaphore>();

private Semaphore getSemaphore(final String key) {
    return semaphoresMap.computeIfAbsent(key, (String absentKey) -> new Semaphore(0));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM