Skip to content

Instantly share code, notes, and snippets.

@stanio
Last active January 10, 2019 13:25
Show Gist options
  • Save stanio/99170fd617b5fb9a51e327a9ac4f0f17 to your computer and use it in GitHub Desktop.
Save stanio/99170fd617b5fb9a51e327a9ac4f0f17 to your computer and use it in GitHub Desktop.
Dynamic lock set
/*
* 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 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 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