【问题标题】:Key based Semaphores with Guava vs Semaphores in a ConcurrentHashMapConcurrentHashMap 中基于键的信号量与番石榴与信号量
【发布时间】:2023-03-25 19:30:02
【问题描述】:

我需要在我的应用程序中使用基于密钥的 Semaphore 机制,并偶然发现了 Guava 的 Striped.semaphore(int, int)。但是,它的行为并不像预期的那样。

使用以下代码,fetch 有时会返回 null。这两种方法都由不同的线程访问。我希望调用 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;
}

如果我使用以下代码切换回基本 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;
}

我在这里缺少什么?谢谢

【问题讨论】:

    标签: java concurrency guava semaphore


    【解决方案1】:

    Guava 的Striped 指定多个键可能映射到同一个信号量。来自 Javadoc:

    此类提供的保证是相等的键导致相同的锁(或信号量),即 if (key1.equals(key2)) then striped.get(key1) == striped.get(key2) (假设Object.hashCode() 正确实现了键)。请注意,如果 key1 不等于 key2,则不能保证 striped.get(key1) != striped.get(key2);然而,这些元素可能被映射到同一个锁。条纹数量越少,发生这种情况的可能性就越高。

    您的代码中的基本假设似乎是,如果与特定对象关联的信号量具有许可,则该对象在映射中具有条目,但事实并非如此 - 如果映射中有条目对于碰巧与同一个Semaphore 关联的另一个对象,则该许可可能由一个完全不同的对象上的fetch 获取,该对象实际上在地图中没有条目。

    【讨论】:

    • 好的,谢谢。当我将 Semaphore 对象打印到控制台时,我意识到了这一点,它为不同的键显示了相同的 Semaphore。那么 Striped 的目的是什么?
    • 因为对于许多应用程序来说,将多个对象映射到同一个锁或信号量是完全可以接受的,当唯一的目标是控制争用,而不是为每个对象拥有独占锁或信号量时。例如,ConcurrentHashMap 使用类似的条带锁定,其中对象被映射到映射的不同段中,每个段在内部完全同步,并且同一段中的对象竞争相同的锁。对整张地图使用单个锁相当于拥有一个条带。
    • 感谢您的解释
    • 您不应该同步(或锁定)“基本 Java”示例的 getSemaphore 方法吗?
    • @eriksmith200 如果您使用的是Striped,则不会。
    【解决方案2】:

    “基本 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));
    }
    

    【讨论】:

      猜你喜欢
      • 2017-08-24
      • 1970-01-01
      • 2010-09-09
      • 2010-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-01
      • 2011-07-25
      相关资源
      最近更新 更多