Created
February 24, 2011 10:25
-
-
Save taichi/842027 to your computer and use it in GitHub Desktop.
ConcurrentMap contains Set. feel easy but not.
This file contains hidden or 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
import java.security.SecureRandom; | |
import java.util.ArrayList; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Set; | |
import java.util.concurrent.Callable; | |
import java.util.concurrent.ConcurrentHashMap; | |
import java.util.concurrent.ConcurrentMap; | |
import java.util.concurrent.ConcurrentSkipListSet; | |
import java.util.concurrent.ExecutorService; | |
import java.util.concurrent.Executors; | |
import java.util.concurrent.Future; | |
import java.util.concurrent.TimeUnit; | |
import java.util.concurrent.locks.Lock; | |
import java.util.concurrent.locks.ReentrantLock; | |
public class LockFree { | |
final ConcurrentMap<String, LockableEntries> map = new ConcurrentHashMap<String, LockableEntries>(); | |
public void add(String key, String value) { | |
Entry newentry = new Entry(value); | |
for (;;) { | |
LockableEntries current = this.map.get(key); | |
if (current == null) { | |
Set<Entry> newset = new ConcurrentSkipListSet<Entry>(); | |
newset.add(newentry); | |
LockableEntries newone = new LockableEntries(newset); | |
LockableEntries previous = this.map.putIfAbsent(key, newone); | |
if ((previous == null) || internalAdd(previous, newentry)) { | |
break; | |
} | |
} else { | |
if (internalAdd(current, newentry)) { | |
break; | |
} | |
} | |
} | |
} | |
private boolean internalAdd(LockableEntries entries, final Entry newone) { | |
return entries.lockIn(new Operation() { | |
@Override | |
public boolean execute(Set<Entry> target) { | |
if (target.isEmpty()) { | |
return false; | |
} else { | |
target.add(newone); | |
return true; | |
} | |
} | |
}); | |
} | |
public Iterable<Entry> get(final String key) { | |
final LockableEntries value = this.map.get(key); | |
if (value == null) { | |
return null; | |
} | |
return new Iterable<Entry>() { | |
@Override | |
public Iterator<Entry> iterator() { | |
return new Iterator<Entry>() { | |
Iterator<Entry> delegate = value.iterator(); | |
@Override | |
public boolean hasNext() { | |
return delegate.hasNext(); | |
} | |
@Override | |
public Entry next() { | |
return delegate.next(); | |
} | |
@Override | |
public void remove() { | |
delegate.remove(); | |
if (delegate.hasNext() == false && value.isEmpty()) { | |
LockFree.this.remove(key); | |
} | |
} | |
}; | |
} | |
}; | |
} | |
public void remove(String key) { | |
LockableEntries removed = this.map.remove(key); | |
if (removed != null) { | |
removed.lockIn(new Operation() { | |
@Override | |
public boolean execute(Set<Entry> target) { | |
target.clear(); | |
return true; | |
} | |
}); | |
} | |
} | |
interface Operation { | |
boolean execute(Set<Entry> target); | |
} | |
class LockableEntries implements Iterable<Entry> { | |
Lock guard = new ReentrantLock(); | |
final Set<Entry> entries; | |
public LockableEntries(Set<Entry> set) { | |
this.entries = set; | |
} | |
boolean lockIn(Operation op) { | |
this.guard.lock(); | |
try { | |
return op.execute(this.entries); | |
} finally { | |
this.guard.unlock(); | |
} | |
} | |
@Override | |
public Iterator<Entry> iterator() { | |
return this.entries.iterator(); | |
} | |
public boolean isEmpty() { | |
return this.entries.isEmpty(); | |
} | |
} | |
class Entry implements Comparable<Entry> { | |
String member; | |
Entry(String s) { | |
if (s == null) { | |
throw new IllegalArgumentException(); | |
} | |
this.member = s; | |
} | |
@Override | |
public boolean equals(Object obj) { | |
return this.member.equals(obj); | |
} | |
@Override | |
public int hashCode() { | |
return this.member.hashCode(); | |
} | |
@Override | |
public int compareTo(Entry o) { | |
return this.member.compareTo(o.member); | |
} | |
} | |
public static void main(String[] args) throws Exception { | |
// sandboxing codes. | |
final LockFree lf = new LockFree(); | |
ExecutorService es = Executors.newFixedThreadPool(1000); | |
final SecureRandom random = new SecureRandom(); | |
random.setSeed(random.generateSeed(20)); | |
final Set<String> mems = new ConcurrentSkipListSet<String>(); | |
List<Callable<String>> list = new ArrayList<Callable<String>>(); | |
for (int i = 0; i < 100; i++) { | |
Callable<String> add = new Callable<String>() { | |
@Override | |
public String call() { | |
try { | |
for (int i = 0; i < 1000; i++) { | |
String key = "aaa" + random.nextInt(100); | |
// System.out.printf("ADD %s %n", key); | |
lf.add(key, "bbb" + random.nextInt(10)); | |
Thread.sleep(random.nextInt(100)); | |
} | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
return "add"; | |
} | |
}; | |
list.add(add); | |
Callable<String> del = new Callable<String>() { | |
@Override | |
public String call() { | |
try { | |
for (int i = 0; i < 1000; i++) { | |
String key = "aaa" + random.nextInt(100); | |
// System.out.printf("DEL %s %n", key); | |
if (i % 2 == 0) { | |
lf.remove(key); | |
} else { | |
Iterable<Entry> ite = lf.get(key); | |
if (ite != null) { | |
for (Iterator<Entry> cur = ite.iterator(); cur | |
.hasNext();) { | |
cur.next(); | |
cur.remove(); | |
} | |
} | |
} | |
Thread.sleep(random.nextInt(100)); | |
} | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
return "del"; | |
} | |
}; | |
list.add(del); | |
Callable<String> see = new Callable<String>() { | |
@Override | |
public String call() throws Exception { | |
for (int i = 0; i < 1000; i++) { | |
String key = "aaa" + random.nextInt(100); | |
Iterable<Entry> ite = lf.get(key); | |
if (ite != null) { | |
for (Entry e : ite) { | |
mems.add(e.member); | |
} | |
} | |
} | |
return "see"; | |
} | |
}; | |
list.add(see); | |
} | |
List<Future<String>> futures = es.invokeAll(list); | |
es.shutdown(); | |
if (es.awaitTermination(3, TimeUnit.MINUTES) == false) { | |
es.shutdownNow(); | |
es.awaitTermination(10, TimeUnit.SECONDS); | |
} | |
int a = 0; | |
int d = 0; | |
int s = 0; | |
for (Future<String> f : futures) { | |
if (f.isDone()) { | |
if ("add".equals(f.get())) { | |
a++; | |
} | |
if ("del".equals(f.get())) { | |
d++; | |
} | |
if ("see".equals(f.get())) { | |
s++; | |
} | |
} | |
} | |
System.out.printf("ADD:[%-4s] DEL:[%-4s] DIFF:[%-4s] SEE:[%-4s]%n", a, | |
d, a - d, s); | |
System.out.printf("MAP:[%s] MEMS:[%s]%n", lf.map.size(), mems.size()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment