Skip to content

Commit 17fc5fe

Browse files
committed
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
1 parent 5a1bb6c commit 17fc5fe

4 files changed

Lines changed: 867 additions & 0 deletions

File tree

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package com.antkorwin.xsync;
2+
3+
import java.util.Objects;
4+
import java.util.concurrent.ConcurrentMap;
5+
import java.util.concurrent.locks.ReadWriteLock;
6+
import java.util.concurrent.locks.ReentrantReadWriteLock;
7+
import java.util.concurrent.locks.StampedLock;
8+
import java.util.function.Supplier;
9+
10+
import org.hibernate.validator.internal.util.ConcurrentReferenceHashMap;
11+
12+
/**
13+
* Created on 22.06.2020.
14+
* <p>
15+
* The factory of {@link ReadWriteLock ReadWriteLocks}, based on
16+
* {@link ConcurrentReferenceHashMap}. Use this if you need to allow
17+
* multiple concurrent readers but only one writer. You can also control
18+
* whether or not to support reentrancy and whether or not the lock should
19+
* be fair. Depending on your usage patterns, you may opt to back the
20+
* locks with a {@link ReentrantReadWriteLock} (default) or a
21+
* {@link StampedLock}. Note, for a stamped lock, the optimistic locking
22+
* model and lock type modification are not supported.
23+
* </p>
24+
*
25+
* @author Carlos Macasaet
26+
*/
27+
public class ReadWriteLockFactory<KeyT> {
28+
29+
private static final int DEFAULT_INITIAL_CAPACITY = 16;
30+
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
31+
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
32+
private static final ConcurrentReferenceHashMap.ReferenceType DEFAULT_REFERENCE_TYPE =
33+
ConcurrentReferenceHashMap.ReferenceType.WEAK;
34+
35+
private final ConcurrentMap<KeyT, ReadWriteLock> map;
36+
private final Supplier<? extends ReadWriteLock> lockSupplier;
37+
38+
/**
39+
* Create a lock factory with default settings
40+
*/
41+
public ReadWriteLockFactory() {
42+
this(DEFAULT_CONCURRENCY_LEVEL, DEFAULT_REFERENCE_TYPE);
43+
}
44+
45+
/**
46+
* Create a lock factory with default settings and a custom lock generator.
47+
*
48+
* @param lockSupplier a method for creating new {@link ReadWriteLock} instances.
49+
*/
50+
public ReadWriteLockFactory(final Supplier<? extends ReadWriteLock> lockSupplier) {
51+
this(DEFAULT_CONCURRENCY_LEVEL, DEFAULT_REFERENCE_TYPE, lockSupplier);
52+
}
53+
54+
/**
55+
* Create a lock factory with custom settings
56+
*
57+
* @param concurrencyLevel the expected number of threads
58+
* that will concurrently write to the map
59+
* @param referenceType the reference type used for entries (soft or weak)
60+
*/
61+
public ReadWriteLockFactory(int concurrencyLevel, ConcurrentReferenceHashMap.ReferenceType referenceType) {
62+
this(concurrencyLevel, referenceType, ReentrantReadWriteLock::new);
63+
}
64+
65+
/**
66+
* Create a lock factory with custom settings
67+
*
68+
* @param concurrencyLevel the expected number of threads
69+
* that will concurrently write to the map
70+
* @param referenceType the reference type used for entries (soft or weak)
71+
* @param lockSupplier a method for creating ReadWriteLock instances
72+
*/
73+
public ReadWriteLockFactory(final int concurrencyLevel, final ConcurrentReferenceHashMap.ReferenceType referenceType,
74+
final Supplier<? extends ReadWriteLock> lockSupplier) {
75+
this(new ConcurrentReferenceHashMap<>(DEFAULT_INITIAL_CAPACITY,
76+
DEFAULT_LOAD_FACTOR,
77+
concurrencyLevel,
78+
referenceType,
79+
referenceType,
80+
null),
81+
lockSupplier);
82+
}
83+
84+
protected ReadWriteLockFactory(final ConcurrentMap<KeyT, ReadWriteLock> map,
85+
final Supplier<? extends ReadWriteLock> lockSupplier) {
86+
Objects.requireNonNull(map, "map must be provided");
87+
Objects.requireNonNull(lockSupplier, "lockSupplier must be provided");
88+
this.map = map;
89+
this.lockSupplier = lockSupplier;
90+
}
91+
92+
/**
93+
* Creates and returns a lock by the key.
94+
* If the lock for this key already exists in the weak-map,
95+
* then returns the same reference of the lock.
96+
*
97+
* @param key object which used as a key for synchronization
98+
* @return lock instance created for this key
99+
*/
100+
public ReadWriteLock getReadWriteLock(KeyT key) {
101+
return this.map.computeIfAbsent(key, k -> lockSupplier.get());
102+
}
103+
104+
/**
105+
* @return count of locks in this factory.
106+
*/
107+
public long size() {
108+
return this.map.size();
109+
}
110+
111+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.antkorwin.xsync;
2+
3+
4+
import java.util.Objects;
5+
import java.util.concurrent.ConcurrentMap;
6+
import java.util.concurrent.locks.StampedLock;
7+
8+
import org.hibernate.validator.internal.util.ConcurrentReferenceHashMap;
9+
10+
11+
/**
12+
* Created on 22.06.2020.
13+
* <p>
14+
* The factory of locks, based on {@link ConcurrentReferenceHashMap}.
15+
* Use this if you require the performance characteristics of
16+
* {@link StampedLock StampedLocks} over other synchronization
17+
* mechanisms.
18+
* </p>
19+
*
20+
* @author Carlos Macasaet
21+
*/
22+
public class StampedLockFactory<KeyT> {
23+
24+
private static final int DEFAULT_INITIAL_CAPACITY = 16;
25+
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
26+
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
27+
private static final ConcurrentReferenceHashMap.ReferenceType DEFAULT_REFERENCE_TYPE =
28+
ConcurrentReferenceHashMap.ReferenceType.WEAK;
29+
30+
private final ConcurrentMap<KeyT, StampedLock> map;
31+
32+
/**
33+
* Create a factory with default settings
34+
*/
35+
public StampedLockFactory() {
36+
this(new ConcurrentReferenceHashMap<>(DEFAULT_INITIAL_CAPACITY,
37+
DEFAULT_LOAD_FACTOR,
38+
DEFAULT_CONCURRENCY_LEVEL,
39+
DEFAULT_REFERENCE_TYPE,
40+
DEFAULT_REFERENCE_TYPE,
41+
null));
42+
}
43+
44+
/**
45+
* Creating a factory with custom settings
46+
*
47+
* @param concurrencyLevel the expected number of threads
48+
* that will concurrently write to the map
49+
* @param referenceType the reference type used for entries (soft or weak)
50+
*/
51+
public StampedLockFactory(int concurrencyLevel,
52+
ConcurrentReferenceHashMap.ReferenceType referenceType) {
53+
this(new ConcurrentReferenceHashMap<>(DEFAULT_INITIAL_CAPACITY,
54+
DEFAULT_LOAD_FACTOR,
55+
concurrencyLevel,
56+
referenceType,
57+
referenceType,
58+
null));
59+
}
60+
61+
protected StampedLockFactory(final ConcurrentMap<KeyT, StampedLock> map) {
62+
Objects.requireNonNull(map, "map must be provided");
63+
this.map = map;
64+
}
65+
66+
/**
67+
* Creates and returns a lock by the key. If the lock for this key
68+
* already exists(or use by another thread), then returns the same
69+
* reference of the lock.
70+
*
71+
* @param key object which used as a key for synchronization
72+
* @return lock instance created for this key
73+
*/
74+
public StampedLock getLock(KeyT key) {
75+
return this.map.computeIfAbsent(key, k -> new StampedLock());
76+
}
77+
78+
/**
79+
* @return count of locks in this factory.
80+
*/
81+
public long size() {
82+
return this.map.size();
83+
}
84+
85+
}

0 commit comments

Comments
 (0)