简体   繁体   中英

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. However, it does not behave as expected.

Using the following code, fetch sometimes returns 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.

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.

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. From the 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). Note that if key1 is not equal to key2, it is not guaranteed that 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.

The 'basic java' example has a potential race condition, computeIfAbsent is an atomic operation and solves this:

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));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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