Last active
January 10, 2019 13:25
-
-
Save stanio/99170fd617b5fb9a51e327a9ac4f0f17 to your computer and use it in GitHub Desktop.
Dynamic lock set
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
/* | |
* This module, both source code and documentation, | |
* is in the Public Domain, and comes with NO WARRANTY. | |
*/ | |
package net.example.concurrent; | |
import java.lang.ref.ReferenceQueue; | |
import java.lang.ref.WeakReference; | |
import java.util.concurrent.ConcurrentHashMap; | |
import java.util.concurrent.ConcurrentMap; | |
/** | |
* A set of objects accessed by a key. The objects are dynamically allocated, | |
* and are subject to garbage collection when no longer in use. It is | |
* guaranteed the same object for a given key is returned as long as it is | |
* {@linkplain <i>strongly reachable</i> java.lang.ref#reachability}: | |
* <pre> | |
* KeyedObjects<Integer, Object> mutexes; | |
* ... | |
* void doSyncJob(Integer id, Runnable job) { | |
* synchronized (mutexes.get(id)) { | |
* job.run(); | |
* } | |
* }</pre> | |
*/ | |
public abstract class KeyedObjects<K, T> { | |
private static class KeyedReference<K, T> extends WeakReference<T> { | |
final K key; | |
KeyedReference(K key, T referent, ReferenceQueue<? super T> queue) { | |
super(referent, queue); | |
this.key = key; | |
} | |
} | |
private final ConcurrentMap<K, KeyedReference<K, T>> objHeap; | |
private final ReferenceQueue<T> refQueue; | |
/** | |
* ... . | |
*/ | |
protected KeyedObjects() { | |
objHeap = new ConcurrentHashMap<>(); | |
refQueue = new ReferenceQueue<>(); | |
} | |
private KeyedReference<K, T> newReference(K key, T obj) { | |
return new KeyedReference<>(key, obj, refQueue); | |
} | |
private void expungeStaleEntries() { | |
KeyedReference<?, ?> ref; | |
while ((ref = (KeyedReference<?, ?>) refQueue.poll()) != null) { | |
objHeap.remove(ref.key, ref); | |
} | |
} | |
/** | |
* ... . | |
* | |
* @param key ... . | |
* @return ... . | |
*/ | |
public T get(K key) { | |
expungeStaleEntries(); | |
while (true) { | |
T value; | |
KeyedReference<K, T> ref = objHeap.get(key); | |
if (ref == null) { | |
value = initValue(key); | |
KeyedReference<K, T> newRef = newReference(key, value); | |
if (objHeap.putIfAbsent(key, newRef) == null) { | |
return value; | |
} | |
} else { | |
value = ref.get(); | |
if (value != null) { | |
return value; | |
} | |
value = initValue(key); | |
KeyedReference<K, T> newRef = newReference(key, value); | |
if (objHeap.replace(key, ref, newRef)) { | |
return value; | |
} | |
} | |
} | |
} | |
/** | |
* ... . | |
* | |
* @param key ... . | |
* @return ... . | |
*/ | |
protected abstract T initValue(K key); | |
} |
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
/* | |
* This module, both source code and documentation, | |
* is in the Public Domain, and comes with NO WARRANTY. | |
*/ | |
package net.example.concurrent; | |
import java.util.concurrent.locks.Lock; | |
import java.util.concurrent.locks.ReentrantLock; | |
/** | |
* A set of dynamically allocated locks. It guarantees the same lock instance | |
* will be returned for a given key, when used concurrently. | |
*/ | |
public abstract class NamedLocks<K, T extends Lock> extends KeyedObjects<K, T> { | |
protected NamedLocks() { | |
//super(); | |
} | |
public static <K> NamedLocks<K, ReentrantLock> newReentrantLocks() { | |
return new NamedLocks<K, ReentrantLock>() { | |
@Override protected ReentrantLock initValue(K key) { | |
return new ReentrantLock(); | |
} | |
}; | |
} | |
public static void main(String[] args) { | |
NamedLocks<String, ? extends Lock> locks = NamedLocks.newReentrantLocks(); | |
Lock lock = locks.get("foo"); | |
lock.lock(); | |
lock.unlock(); | |
NamedLocks<Integer, ReentrantLock> numberedLocks = NamedLocks.newReentrantLocks(); | |
ReentrantLock lock2 = numberedLocks.get(1); | |
lock2.getHoldCount(); | |
} | |
} |
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
/* | |
* This module, both source code and documentation, | |
* is in the Public Domain, and comes with NO WARRANTY. | |
*/ | |
package net.example.concurrent; | |
import java.util.concurrent.locks.Lock; | |
public class NamedLocksTest { | |
public static void main(String[] args) throws Exception { | |
test1(); | |
System.out.println("---"); | |
test2(); | |
System.out.println("---"); | |
test3(); | |
System.out.println("---"); | |
test4(); | |
} | |
private static NamedLocks<String, ? extends Lock> locks = NamedLocks.newReentrantLocks(); | |
/* | |
* First to get: java.util.concurrent.locks.ReentrantLock@5fb57890[Locked by thread main] | |
* Got it: java.util.concurrent.locks.ReentrantLock@5fb57890[Locked by thread concurrent] | |
* Finally: java.util.concurrent.locks.ReentrantLock@6f93ee4[Unlocked] | |
*/ | |
static void test1() throws Exception { | |
Thread concurrent = common(); | |
Thread.sleep(100); | |
System.gc(); | |
System.out.println("Finally: \t" + locks.get(new String("foo"))); | |
concurrent.join(); | |
} | |
/* | |
* First to get: java.util.concurrent.locks.ReentrantLock@6f93ee4[Locked by thread main] | |
* Got it: java.util.concurrent.locks.ReentrantLock@6f93ee4[Locked by thread concurrent] | |
* Finally: java.util.concurrent.locks.ReentrantLock@6f93ee4[Locked by thread concurrent] | |
*/ | |
static void test2() throws Exception { | |
Thread concurrent = common(); | |
System.gc(); | |
System.out.println("Finally: \t" + locks.get(new String("foo"))); | |
concurrent.join(); | |
} | |
/* | |
* First to get: java.util.concurrent.locks.ReentrantLock@6f93ee4[Locked by thread main] | |
* Got it: java.util.concurrent.locks.ReentrantLock@6f93ee4[Locked by thread concurrent] | |
* Finally: java.util.concurrent.locks.ReentrantLock@507d811a[Unlocked] | |
*/ | |
static void test3() throws Exception { | |
Thread concurrent = common(); | |
concurrent.join(); | |
System.gc(); | |
System.out.println("Finally: \t" + locks.get(new String("foo"))); | |
} | |
/* | |
* First to get: java.util.concurrent.locks.ReentrantLock@507d811a[Locked by thread main] | |
* Got it: java.util.concurrent.locks.ReentrantLock@66f11de2[Locked by thread concurrent] | |
* Finally: java.util.concurrent.locks.ReentrantLock@66f11de2[Unlocked] | |
*/ | |
static void test4() throws Exception { | |
Thread concurrent = common(); | |
System.gc(); | |
concurrent.join(); | |
System.out.println("Finally: \t" + locks.get(new String("foo"))); | |
} | |
private static final Thread common() { | |
Thread concurrent = new Thread(new Runnable() { | |
@Override | |
public void run() { | |
Lock lock = locks.get(new String("foo")); | |
lock.lock(); | |
try { | |
System.out.println("Got it: \t" + lock); | |
} finally { | |
lock.unlock(); | |
} | |
} | |
}, "concurrent"); | |
Lock lock = locks.get(new String("foo")); | |
lock.lock(); | |
try { | |
concurrent.start(); | |
Thread.yield(); | |
System.out.println("First to get: \t" + lock); | |
} finally { | |
lock.unlock(); | |
lock = null; | |
} | |
return concurrent; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment