Skip to content

Instantly share code, notes, and snippets.

@taichi
Created February 24, 2011 10:25
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save taichi/842027 to your computer and use it in GitHub Desktop.
Save taichi/842027 to your computer and use it in GitHub Desktop.
ConcurrentMap contains Set. feel easy but not.
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