-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add StampedLockFactory and ReadWriteLockFactory
This adds two new factories that generate `StampedLock` and `ReadWriteLock` instances for applications that require more granular control over locking. The unit tests duplicate the verifications in `XMutexFactoryImplTest` and also introduce verifications specific to the synchronisation mechanisms. Addresses: #9
- Loading branch information
Showing
4 changed files
with
867 additions
and
0 deletions.
There are no files selected for viewing
111 changes: 111 additions & 0 deletions
111
src/main/java/com/antkorwin/xsync/ReadWriteLockFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
package com.antkorwin.xsync; | ||
|
||
import java.util.Objects; | ||
import java.util.concurrent.ConcurrentMap; | ||
import java.util.concurrent.locks.ReadWriteLock; | ||
import java.util.concurrent.locks.ReentrantReadWriteLock; | ||
import java.util.concurrent.locks.StampedLock; | ||
import java.util.function.Supplier; | ||
|
||
import org.hibernate.validator.internal.util.ConcurrentReferenceHashMap; | ||
|
||
/** | ||
* Created on 22.06.2020. | ||
* <p> | ||
* The factory of {@link ReadWriteLock ReadWriteLocks}, based on | ||
* {@link ConcurrentReferenceHashMap}. Use this if you need to allow | ||
* multiple concurrent readers but only one writer. You can also control | ||
* whether or not to support reentrancy and whether or not the lock should | ||
* be fair. Depending on your usage patterns, you may opt to back the | ||
* locks with a {@link ReentrantReadWriteLock} (default) or a | ||
* {@link StampedLock}. Note, for a stamped lock, the optimistic locking | ||
* model and lock type modification are not supported. | ||
* </p> | ||
* | ||
* @author Carlos Macasaet | ||
*/ | ||
public class ReadWriteLockFactory<KeyT> { | ||
|
||
private static final int DEFAULT_INITIAL_CAPACITY = 16; | ||
private static final float DEFAULT_LOAD_FACTOR = 0.75f; | ||
private static final int DEFAULT_CONCURRENCY_LEVEL = 16; | ||
private static final ConcurrentReferenceHashMap.ReferenceType DEFAULT_REFERENCE_TYPE = | ||
ConcurrentReferenceHashMap.ReferenceType.WEAK; | ||
|
||
private final ConcurrentMap<KeyT, ReadWriteLock> map; | ||
private final Supplier<? extends ReadWriteLock> lockSupplier; | ||
|
||
/** | ||
* Create a lock factory with default settings | ||
*/ | ||
public ReadWriteLockFactory() { | ||
this(DEFAULT_CONCURRENCY_LEVEL, DEFAULT_REFERENCE_TYPE); | ||
} | ||
|
||
/** | ||
* Create a lock factory with default settings and a custom lock generator. | ||
* | ||
* @param lockSupplier a method for creating new {@link ReadWriteLock} instances. | ||
*/ | ||
public ReadWriteLockFactory(final Supplier<? extends ReadWriteLock> lockSupplier) { | ||
this(DEFAULT_CONCURRENCY_LEVEL, DEFAULT_REFERENCE_TYPE, lockSupplier); | ||
} | ||
|
||
/** | ||
* Create a lock factory with custom settings | ||
* | ||
* @param concurrencyLevel the expected number of threads | ||
* that will concurrently write to the map | ||
* @param referenceType the reference type used for entries (soft or weak) | ||
*/ | ||
public ReadWriteLockFactory(int concurrencyLevel, ConcurrentReferenceHashMap.ReferenceType referenceType) { | ||
this(concurrencyLevel, referenceType, ReentrantReadWriteLock::new); | ||
} | ||
|
||
/** | ||
* Create a lock factory with custom settings | ||
* | ||
* @param concurrencyLevel the expected number of threads | ||
* that will concurrently write to the map | ||
* @param referenceType the reference type used for entries (soft or weak) | ||
* @param lockSupplier a method for creating ReadWriteLock instances | ||
*/ | ||
public ReadWriteLockFactory(final int concurrencyLevel, final ConcurrentReferenceHashMap.ReferenceType referenceType, | ||
final Supplier<? extends ReadWriteLock> lockSupplier) { | ||
this(new ConcurrentReferenceHashMap<>(DEFAULT_INITIAL_CAPACITY, | ||
DEFAULT_LOAD_FACTOR, | ||
concurrencyLevel, | ||
referenceType, | ||
referenceType, | ||
null), | ||
lockSupplier); | ||
} | ||
|
||
protected ReadWriteLockFactory(final ConcurrentMap<KeyT, ReadWriteLock> map, | ||
final Supplier<? extends ReadWriteLock> lockSupplier) { | ||
Objects.requireNonNull(map, "map must be provided"); | ||
Objects.requireNonNull(lockSupplier, "lockSupplier must be provided"); | ||
this.map = map; | ||
this.lockSupplier = lockSupplier; | ||
} | ||
|
||
/** | ||
* Creates and returns a lock by the key. | ||
* If the lock for this key already exists in the weak-map, | ||
* then returns the same reference of the lock. | ||
* | ||
* @param key object which used as a key for synchronization | ||
* @return lock instance created for this key | ||
*/ | ||
public ReadWriteLock getReadWriteLock(KeyT key) { | ||
return this.map.computeIfAbsent(key, k -> lockSupplier.get()); | ||
} | ||
|
||
/** | ||
* @return count of locks in this factory. | ||
*/ | ||
public long size() { | ||
return this.map.size(); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package com.antkorwin.xsync; | ||
|
||
|
||
import java.util.Objects; | ||
import java.util.concurrent.ConcurrentMap; | ||
import java.util.concurrent.locks.StampedLock; | ||
|
||
import org.hibernate.validator.internal.util.ConcurrentReferenceHashMap; | ||
|
||
|
||
/** | ||
* Created on 22.06.2020. | ||
* <p> | ||
* The factory of locks, based on {@link ConcurrentReferenceHashMap}. | ||
* Use this if you require the performance characteristics of | ||
* {@link StampedLock StampedLocks} over other synchronization | ||
* mechanisms. | ||
* </p> | ||
* | ||
* @author Carlos Macasaet | ||
*/ | ||
public class StampedLockFactory<KeyT> { | ||
|
||
private static final int DEFAULT_INITIAL_CAPACITY = 16; | ||
private static final float DEFAULT_LOAD_FACTOR = 0.75f; | ||
private static final int DEFAULT_CONCURRENCY_LEVEL = 16; | ||
private static final ConcurrentReferenceHashMap.ReferenceType DEFAULT_REFERENCE_TYPE = | ||
ConcurrentReferenceHashMap.ReferenceType.WEAK; | ||
|
||
private final ConcurrentMap<KeyT, StampedLock> map; | ||
|
||
/** | ||
* Create a factory with default settings | ||
*/ | ||
public StampedLockFactory() { | ||
this(new ConcurrentReferenceHashMap<>(DEFAULT_INITIAL_CAPACITY, | ||
DEFAULT_LOAD_FACTOR, | ||
DEFAULT_CONCURRENCY_LEVEL, | ||
DEFAULT_REFERENCE_TYPE, | ||
DEFAULT_REFERENCE_TYPE, | ||
null)); | ||
} | ||
|
||
/** | ||
* Creating a factory with custom settings | ||
* | ||
* @param concurrencyLevel the expected number of threads | ||
* that will concurrently write to the map | ||
* @param referenceType the reference type used for entries (soft or weak) | ||
*/ | ||
public StampedLockFactory(int concurrencyLevel, | ||
ConcurrentReferenceHashMap.ReferenceType referenceType) { | ||
this(new ConcurrentReferenceHashMap<>(DEFAULT_INITIAL_CAPACITY, | ||
DEFAULT_LOAD_FACTOR, | ||
concurrencyLevel, | ||
referenceType, | ||
referenceType, | ||
null)); | ||
} | ||
|
||
protected StampedLockFactory(final ConcurrentMap<KeyT, StampedLock> map) { | ||
Objects.requireNonNull(map, "map must be provided"); | ||
this.map = map; | ||
} | ||
|
||
/** | ||
* Creates and returns a lock by the key. If the lock for this key | ||
* already exists(or use by another thread), then returns the same | ||
* reference of the lock. | ||
* | ||
* @param key object which used as a key for synchronization | ||
* @return lock instance created for this key | ||
*/ | ||
public StampedLock getLock(KeyT key) { | ||
return this.map.computeIfAbsent(key, k -> new StampedLock()); | ||
} | ||
|
||
/** | ||
* @return count of locks in this factory. | ||
*/ | ||
public long size() { | ||
return this.map.size(); | ||
} | ||
|
||
} |
Oops, something went wrong.