package org.threadly.concurrent.lock; import java.util.concurrent.ConcurrentHashMap; import org.threadly.util.ArgumentVerifier; /** * This structure allows for more controlled levels of parallelism. It helps in allowing threads * to only lock when their interest are the same. It is guaranteed that every key provided will * return the same lock. But it is not guaranteed that two different keys will not have the same * lock. * <p> * Currently this class only provides objects that should be synchronized on. Compared to * {@link java.util.concurrent.locks.Lock} objects which have {@code lock()}, {@code tryLock()}, * and {@code unlock()} functionality. This choice was primarily because of the way the internals * of threadly work. * * @since 1.0.0 */ public class StripedLock { private final int expectedConcurrencyLevel; private final ConcurrentHashMap<Integer, Object> locks; /** * Constructs a new {@link StripedLock} with a given expected concurrency level. The higher the * concurrency level, the less lock contention will exist, but more locks will have to be * synchronized on and more memory will be used to store the locks. * * @param expectedConcurrencyLevel expected level of parallelism */ public StripedLock(int expectedConcurrencyLevel) { ArgumentVerifier.assertGreaterThanZero(expectedConcurrencyLevel, "expectedConcurrencyLevel"); this.expectedConcurrencyLevel = expectedConcurrencyLevel; this.locks = new ConcurrentHashMap<>(); } /** * Getter for the expected concurrency level this class was constructed with. * * @return the constructed level of concurrency */ public int getExpectedConcurrencyLevel() { return expectedConcurrencyLevel; } /** * Call to get a lock object for a given key. * * @param key to use {@code hashCode()} from to determine lock * @return consistent Object for a given key */ public Object getLock(Object key) { if (key == null) { return getLock(0); } else { return getLock(key.hashCode()); } } /** * Call to get a lock object for a given hash code. * * @param hashCode to use to determine which lock to return * @return consistent Object for a given hash code */ public Object getLock(int hashCode) { int lockIndex = Math.abs(hashCode) % expectedConcurrencyLevel; Object result = locks.get(lockIndex); if (result == null) { result = new Object(); Object putIfAbsentResult = locks.putIfAbsent(lockIndex, result); if (putIfAbsentResult != null) { result = putIfAbsentResult; } } return result; } }