Java tutorial
/* * RefinableHashSet.java * * Created on November 15, 2006, 3:59 PM * * From "The Art of Multiprocessor Programming", * by Maurice Herlihy and Nir Shavit. * * This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License. * http://i.creativecommons.org/l/by-sa/3.0/us/88x31.png */ //package xbird.util.concurrent.set; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicMarkableReference; import java.util.concurrent.locks.ReentrantLock; /** * Concurrent hash set that allows the lock array to be resized. * @param <T> type * @author Maurice Herlihy */ public class RefinableHashSet<T> extends BaseHashSet<T> { AtomicMarkableReference<Thread> owner; volatile ReentrantLock[] locks; /** * Concurrent Cuckoo hash set. Resizes lock array. * @param capacity Initial number of buckets. */ public RefinableHashSet(int capacity) { super(capacity); locks = new ReentrantLock[capacity]; for (int j = 0; j < capacity; j++) { locks[j] = new ReentrantLock(); } owner = new AtomicMarkableReference<Thread>(null, false); } /** * Synchronize before adding, removing, or testing for item * @param x item involved */ @Override public void acquire(T x) { boolean[] mark = { true }; Thread me = Thread.currentThread(); Thread who; while (true) { do { // wait until not resizing who = owner.get(mark); } while (mark[0] && who != me); ReentrantLock[] oldLocks = this.locks; int myBucket = Math.abs(x.hashCode() % oldLocks.length); ReentrantLock oldLock = oldLocks[myBucket]; oldLock.lock(); // acquire lock who = owner.get(mark); if ((!mark[0] || who == me) && this.locks == oldLocks) { // recheck return; } else { // unlock & try again oldLock.unlock(); } } } /** * synchronize after adding, removing, or testing for item * @param x item involved */ @Override public void release(T x) { int myBucket = Math.abs(x.hashCode() % locks.length); locks[myBucket].unlock(); } /** * Ensure that no thread is currently locking the set. */ protected void quiesce() { for (ReentrantLock lock : locks) { while (lock.isLocked()) { } // spin } } /** * double the set size */ @Override public void resize() { int oldCapacity = table.length; int newCapacity = 2 * oldCapacity; Thread me = Thread.currentThread(); if (owner.compareAndSet(null, me, false, true)) { try { if (table.length != oldCapacity) { // someone else resized first return; } quiesce(); List<T>[] oldTable = table; table = (List<T>[]) new List[newCapacity]; for (int i = 0; i < newCapacity; i++) table[i] = new ArrayList<T>(); locks = new ReentrantLock[newCapacity]; for (int j = 0; j < locks.length; j++) { locks[j] = new ReentrantLock(); } initializeFrom(oldTable); } finally { owner.set(null, false); // restore prior state } } } @Override public boolean policy() { return size / table.length > 4; } private void initializeFrom(List<T>[] oldTable) { for (List<T> bucket : oldTable) { for (T x : bucket) { int myBucket = Math.abs(x.hashCode() % table.length); table[myBucket].add(x); } } } } /** * Simple fine-grained hash map. * @param <T> type * @author Maurice Herlihy */ abstract class BaseHashSet<T> { protected List<T>[] table; protected int size; public BaseHashSet(int capacity) { size = 0; table = (List<T>[]) new List[capacity]; for (int i = 0; i < capacity; i++) { table[i] = new ArrayList<T>(); } } /** * Is item in set? * @param x item to test * @return <code>true</code> iff item present */ public boolean contains(T x) { acquire(x); try { int myBucket = Math.abs(x.hashCode() % table.length); return table[myBucket].contains(x); } finally { release(x); } } /** * Add item to set * @param x item to add * @return <code>true</code> iff set changed */ public boolean add(T x) { boolean result = false; acquire(x); try { int myBucket = Math.abs(x.hashCode() % table.length); result = table[myBucket].add(x); size = result ? size + 1 : size; } finally { release(x); // always unlock } if (policy()) resize(); return result; } /** * Remove item from set * @param x item to remove * @return <code>true</code> iff set changed */ public boolean remove(T x) { acquire(x); try { int myBucket = Math.abs(x.hashCode() % table.length); boolean result = table[myBucket].remove(x); size = result ? size - 1 : size; return result; } finally { release(x); // always unlock } } /** * Synchronize before adding, removing, or testing for item * @param x item involved */ public abstract void acquire(T x); /** * synchronize after adding, removing, or testing for item * @param x item involved */ public abstract void release(T x); /** * double the set size */ public abstract void resize(); /** * decide whether to resize * @return whether to resize */ public abstract boolean policy(); }