//package com.tomergabel.util;
import java.util.concurrent.Callable;
/**
* Utility class that provides a lazy initialization object wrapper.
* <p/>
* To use this wrapper simply implement {@link java.util.concurrent.Callable#call()} and return the appropriate value.
* Exceptions are propagated by {@link #get()} as {@link com.tomergabel.util.LazyInitializationException}s.
* <p/>
* <strong>Note: This wrapper is <em>not</em> thread-safe!</strong>
*
* @param <T> The type wrapped by the lazy initializer.
*/
public abstract class Lazy<T> implements Callable<T> {
/**
* The actual value.
*/
private T value = null;
/**
* Protected c'tor (to prevent direct initialization.
*/
protected Lazy() {
}
/**
* A private c'tor used to pre-cache the value. This is used by the convenience method {@link #from(Object)} to
* create a "const" lazy initializer.
*
* @param value The actual value.
*/
private Lazy( final T value ) {
this.value = value;
}
/**
* Retreives the lazy-loaded value, initializing it on the fly if necessary.
*
* @return The lazy-loaded value.
* @throws LazyInitializationException An error has occurred while initializing the lazy-loaded value. Please see
* the exception {@link Exception#getCause() cause} for detials.
*/
public T get() throws LazyInitializationException {
try {
return this.value == null ? ( this.value = this.call() ) : this.value;
} catch ( Exception e ) {
throw new LazyInitializationException( e );
}
}
/**
* Wraps a value with a pre-cached value. This is a convenience method intended to provide an easy way to specify
* constant values to a lazy-loaded placeholder if necessary.
*
* @param value The actual value.
* @param <T> The type wrapped by the lazy initializer.
* @return A {@link Lazy} instance for the specified value.
*/
public static <T> Lazy<T> from( final T value ) {
return new Lazy<T>( value ) {
@Override
public T call() throws Exception {
// Safety net, should never happen
throw new IllegalStateException( "Lazy initializer should be not called on lazy constants." );
}
};
}
/**
* Wraps the specified callable with a {@link Lazy} instance. This is an alternative to extending this class.
*
* @param initializer The initializer. Calling {@link #from} with {@literal null} for an argument will resolve here,
* so specifying a {@literal null} initializer is the same as specifying a callable which returns
* {@literal null}.
* @return A {@link Lazy} instance for the specified initializer.
*/
@SuppressWarnings( { "unchecked" } )
public static <T> Lazy<T> from( final Callable<T> initializer ) {
return new Lazy<T>() {
@Override
public T call() throws Exception {
// Lazy.from( null ) will resolve here, so (as a convenience) we support
// return of null values
return initializer == null ? null : initializer.call();
}
};
}
}
class LazyInitializationException extends Exception {
public LazyInitializationException( final String message, final Throwable cause ) {
super( message, cause );
}
public LazyInitializationException( final Throwable cause ) {
super( cause );
}
}
import java.lang.ref.WeakReference;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicReference;
public class LazyLoadingReference<T> {
protected Factory<T> factory;
protected AtomicReference<WeakReference<Future<T>>> reference = new AtomicReference<WeakReference<Future<T>>>();
public LazyLoadingReference(Factory<T> factory) {
this.factory = factory;
}
public T get() throws IllegalStateException {
while (true) {
WeakReference<Future<T>> ref = reference.get();
boolean valid = true;
if (ref == null) {
FutureTask<T> f = new FutureTask<T>(new Callable<T>() {
@Override
public T call() throws Exception {
return factory.create();
}
});
ref = new WeakReference<Future<T>>(f);
if (valid = reference.compareAndSet(null, ref)) {
f.run();
}
}
if (valid) {
try {
Future<T> f = ref.get();
if (f != null) {
return f.get();
} else {
reference.compareAndSet(ref, null);
}
} catch (CancellationException e) {
reference.compareAndSet(ref, null);
} catch (ExecutionException e) {
throw new IllegalStateException(e.getCause());
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
}
}
public interface Factory<T> {
T create() throws CancellationException, Exception;
}
}
*/
package org.t2framework.commons.util;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CancellationException;
import org.t2framework.commons.cache.Cache;
import org.t2framework.commons.cache.CacheFactory;
import org.t2framework.commons.cache.CacheType;
import junit.framework.TestCase;
public class LazyLoadingReferenceTest extends TestCase {
public void test_loadSimple() throws Exception {
LazyLoadingReference<String> target = new LazyLoadingReference<String>(
new LazyLoadingReference.Factory<String>() {
public String create() {
return "hoge";
}
});
assertEquals("hoge", target.get());
}
public void test_loadSimple2() throws Exception {
LazyLoadingReference<Cache<String, String>> t2 = new LazyLoadingReference<Cache<String, String>>(
new LazyLoadingReference.Factory<Cache<String, String>>() {
@Override
public Cache<String, String> create()
throws CancellationException {
Cache<String, String> c = CacheFactory
.createCache(CacheType.DEFAULT);
c.put("aaa", "bbb");
return c;
}
});
Cache<String, String> cache = t2.get();
assertNotNull(cache);
assertNotNull(cache.get("aaa"));
}
public void test_exception() throws Exception {
final Exception e = new Exception();
LazyLoadingReference<String> target = new LazyLoadingReference<String>(
new LazyLoadingReference.Factory<String>() {
public String create() throws Exception {
throw e;
}
});
try {
target.get();
fail();
} catch (IllegalStateException t) {
assertNotNull(t.getCause());
assertTrue(t.getCause().getClass() == Exception.class);
assertEquals(e, t.getCause());
}
}
public void test_loadHeavy() throws Exception {
final LazyLoadingReference<String> target = new LazyLoadingReference<String>(
new LazyLoadingReference.Factory<String>() {
public String create() {
return "hoge";
}
});
final Random random = new Random(System.currentTimeMillis());
List<Thread> list = new ArrayList<Thread>();
for (int i = 0; i < 20; i++) {
Runnable r = new Runnable() {
public void run() {
try {
long l = random.nextLong() % 100;
Thread.sleep(l < 0 ? l * -1 : l);
assertEquals("hoge", target.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread t = new Thread(r);
list.add(t);
t.start();
}
for (Thread t : list) {
t.join();
}
}
}
//package edu.ucla.sspace.util;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.BlockingQueue;
/**
* A daemon thread that continuously dequeues {@code Runnable} instances from a
* queue and executes them. This class is intended to be used with a {@link
* java.util.concurrent.Semaphore Semaphore}, whereby work is added the to the
* queue and the semaphore indicates when processing has finished.
*
* @author David Jurgens
*/
public class WorkerThread extends Thread {
/**
* A static variable to indicate which instance of the class the current
* thread is in its name.
*/
private static int threadInstanceCount;
/**
* The queue from which work items will be taken
*/
private final BlockingQueue<Runnable> workQueue;
/**
* An internal queue that holds thread-local tasks. This queue is intended
* to hold multiple tasks to avoid thread contention on the work queue.
*/
private final Queue<Runnable> internalQueue;
/**
* The number of items that should be queued to run by this thread at once.
*/
private final int threadLocalItems;
/**
* Creates a thread that continuously dequeues from the {@code workQueue} at
* once and excutes each item.
*/
public WorkerThread(BlockingQueue<Runnable> workQueue) {
this(workQueue, 1);
}
/**
* Creates a thread that continuously dequeues {@code threadLocalItems} from
* {@code workQueue} at once and excutes them sequentially.
*
* @param threadLocalItems the number of items this thread should dequeue
* from the work queue at one time. Setting this value too high can
* result in a loss of concurrency; setting it too low can result in
* high contention on the work queue if the time per task is also
* low.
*/
public WorkerThread(BlockingQueue<Runnable> workQueue,
int threadLocalItems) {
this.workQueue = workQueue;
this.threadLocalItems = threadLocalItems;
internalQueue = new ArrayDeque<Runnable>();
setDaemon(true);
synchronized(WorkerThread.class) {
setName("WorkerThread-" + (threadInstanceCount++));
}
}
/**
* Continuously dequeues {@code Runnable} instances from the work queue and
* execute them.
*/
public void run() {
Runnable r = null;
while (true) {
// Try to drain the maximum capacity of thread-local items, checking
// whether any were available
if (workQueue.drainTo(internalQueue, threadLocalItems) == 0) {
// block until a work item is available
try {
internalQueue.offer(workQueue.take());
} catch (InterruptedException ie) {
throw new Error(ie);
}
}
// Execute all of the thread-local items
while ((r = internalQueue.poll()) != null)
r.run();
}
}
}
//package com.tomergabel.util;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
/**
* A static container class for URI-related utility functions.
*/
public final class UriUtils {
/**
* A static URI which, when resolved against another URI, returns the other URI's parent.
*/
private static final URI up = URI.create( ".." );
/**
* Returns the parent of the specified URI.
* <p/>
* For example, applying this method to the URI "<tt>http://www.site.com/articles/article.html</tt>" will
* return the URI for "<tt>http://www.site.com/articles/</tt>".
*
* @param uri The URI for which to return the parent.
* @return The parent of the specified URI.
* @throws IllegalArgumentException <ul> <li>The URI cannot be null></li> <li>Can't resolve parent for the specified
* URI.</li> </ul>
*/
public static URI getParent( final URI uri ) throws IllegalArgumentException {
if ( uri == null )
throw new IllegalArgumentException( "The URI cannot be null." );
final String path = uri.toString();
final int finalSeparator = Math.max( path.lastIndexOf( '/' ), path.lastIndexOf( '\\' ) );
final int extension = path.lastIndexOf( '.' );
if ( extension > finalSeparator )
try {
// Extract all but final segment
return new URI( path.substring( 0, finalSeparator + 1 ) ).normalize();
} catch ( URISyntaxException e ) {
throw new IllegalArgumentException( "Can't resolve parent for the specified URI.", e );
}
else
return uri.resolve( up );
}
}
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Deque;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicReference;
public class ConcurrentDoublyLinkedList<E> extends AbstractCollection<E>
implements java.io.Serializable {
/**
* Returns true if given reference is non-null and isn't a header, trailer,
* or marker.
*
* @param n
* (possibly null) node
* @return true if n exists as a user node
*/
private static boolean usable(Node<?> n) {
return n != null && !n.isSpecial();
}
/**
* Throws NullPointerException if argument is null
*
* @param v
* the element
*/
private static void checkNullArg(Object v) {
if (v == null)
throw new NullPointerException();
}
/**
* Returns element unless it is null, in which case throws
* NoSuchElementException.
*
* @param v
* the element
* @return the element
*/
private E screenNullResult(E v) {
if (v == null)
throw new NoSuchElementException();
return v;
}
/**
* Creates an array list and fills it with elements of this list. Used by
* toArray.
*
* @return the arrayList
*/
private ArrayList<E> toArrayList() {
ArrayList<E> c = new ArrayList<E>();
for (Node<E> n = header.forward(); n != null; n = n.forward())
c.add(n.element);
return c;
}
// Fields and constructors
private static final long serialVersionUID = 876323262645176354L;
/**
* List header. First usable node is at header.forward().
*/
private final Node<E> header;
/**
* List trailer. Last usable node is at trailer.back().
*/
private final Node<E> trailer;
/**
* Constructs an empty deque.
*/
public ConcurrentDoublyLinkedList() {
Node h = new Node(null, null, null);
Node t = new Node(null, null, h);
h.setNext(t);
header = h;
trailer = t;
}
/**
* Constructs a deque containing the elements of the specified collection,
* in the order they are returned by the collection's iterator.
*
* @param c
* the collection whose elements are to be placed into this
* deque.
* @throws NullPointerException
* if <tt>c</tt> or any element within it is <tt>null</tt>
*/
public ConcurrentDoublyLinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
/**
* Prepends the given element at the beginning of this deque.
*
* @param o
* the element to be inserted at the beginning of this deque.
* @throws NullPointerException
* if the specified element is <tt>null</tt>
*/
public void addFirst(E o) {
checkNullArg(o);
while (header.append(o) == null)
;
}
/**
* Appends the given element to the end of this deque. This is identical in
* function to the <tt>add</tt> method.
*
* @param o
* the element to be inserted at the end of this deque.
* @throws NullPointerException
* if the specified element is <tt>null</tt>
*/
public void addLast(E o) {
checkNullArg(o);
while (trailer.prepend(o) == null)
;
}
/**
* Prepends the given element at the beginning of this deque.
*
* @param o
* the element to be inserted at the beginning of this deque.
* @return <tt>true</tt> always
* @throws NullPointerException
* if the specified element is <tt>null</tt>
*/
public boolean offerFirst(E o) {
addFirst(o);
return true;
}
/**
* Appends the given element to the end of this deque. (Identical in
* function to the <tt>add</tt> method; included only for consistency.)
*
* @param o
* the element to be inserted at the end of this deque.
* @return <tt>true</tt> always
* @throws NullPointerException
* if the specified element is <tt>null</tt>
*/
public boolean offerLast(E o) {
addLast(o);
return true;
}
/**
* Retrieves, but does not remove, the first element of this deque, or
* returns null if this deque is empty.
*
* @return the first element of this queue, or <tt>null</tt> if empty.
*/
public E peekFirst() {
Node<E> n = header.successor();
return (n == null) ? null : n.element;
}
/**
* Retrieves, but does not remove, the last element of this deque, or
* returns null if this deque is empty.
*
* @return the last element of this deque, or <tt>null</tt> if empty.
*/
public E peekLast() {
Node<E> n = trailer.predecessor();
return (n == null) ? null : n.element;
}
/**
* Returns the first element in this deque.
*
* @return the first element in this deque.
* @throws NoSuchElementException
* if this deque is empty.
*/
public E getFirst() {
return screenNullResult(peekFirst());
}
/**
* Returns the last element in this deque.
*
* @return the last element in this deque.
* @throws NoSuchElementException
* if this deque is empty.
*/
public E getLast() {
return screenNullResult(peekLast());
}
/**
* Retrieves and removes the first element of this deque, or returns null if
* this deque is empty.
*
* @return the first element of this deque, or <tt>null</tt> if empty.
*/
public E pollFirst() {
for (;;) {
Node<E> n = header.successor();
if (!usable(n))
return null;
if (n.delete())
return n.element;
}
}
/**
* Retrieves and removes the last element of this deque, or returns null if
* this deque is empty.
*
* @return the last element of this deque, or <tt>null</tt> if empty.
*/
public E pollLast() {
for (;;) {
Node<E> n = trailer.predecessor();
if (!usable(n))
return null;
if (n.delete())
return n.element;
}
}
/**
* Removes and returns the first element from this deque.
*
* @return the first element from this deque.
* @throws NoSuchElementException
* if this deque is empty.
*/
public E removeFirst() {
return screenNullResult(pollFirst());
}
/**
* Removes and returns the last element from this deque.
*
* @return the last element from this deque.
* @throws NoSuchElementException
* if this deque is empty.
*/
public E removeLast() {
return screenNullResult(pollLast());
}
// *** Queue and stack methods ***
public boolean offer(E e) {
return offerLast(e);
}
public boolean add(E e) {
return offerLast(e);
}
public E poll() {
return pollFirst();
}
public E remove() {
return removeFirst();
}
public E peek() {
return peekFirst();
}
public E element() {
return getFirst();
}
public void push(E e) {
addFirst(e);
}
public E pop() {
return removeFirst();
}
/**
* Removes the first element <tt>e</tt> such that <tt>o.equals(e)</tt>,
* if such an element exists in this deque. If the deque does not contain
* the element, it is unchanged.
*
* @param o
* element to be removed from this deque, if present.
* @return <tt>true</tt> if the deque contained the specified element.
* @throws NullPointerException
* if the specified element is <tt>null</tt>
*/
public boolean removeFirstOccurrence(Object o) {
checkNullArg(o);
for (;;) {
Node<E> n = header.forward();
for (;;) {
if (n == null)
return false;
if (o.equals(n.element)) {
if (n.delete())
return true;
else
break; // restart if interference
}
n = n.forward();
}
}
}
/**
* Removes the last element <tt>e</tt> such that <tt>o.equals(e)</tt>,
* if such an element exists in this deque. If the deque does not contain
* the element, it is unchanged.
*
* @param o
* element to be removed from this deque, if present.
* @return <tt>true</tt> if the deque contained the specified element.
* @throws NullPointerException
* if the specified element is <tt>null</tt>
*/
public boolean removeLastOccurrence(Object o) {
checkNullArg(o);
for (;;) {
Node<E> s = trailer;
for (;;) {
Node<E> n = s.back();
if (s.isDeleted() || (n != null && n.successor() != s))
break; // restart if pred link is suspect.
if (n == null)
return false;
if (o.equals(n.element)) {
if (n.delete())
return true;
else
break; // restart if interference
}
s = n;
}
}
}
/**
* Returns <tt>true</tt> if this deque contains at least one element
* <tt>e</tt> such that <tt>o.equals(e)</tt>.
*
* @param o
* element whose presence in this deque is to be tested.
* @return <tt>true</tt> if this deque contains the specified element.
*/
public boolean contains(Object o) {
if (o == null)
return false;
for (Node<E> n = header.forward(); n != null; n = n.forward())
if (o.equals(n.element))
return true;
return false;
}
/**
* Returns <tt>true</tt> if this collection contains no elements.
* <p>
*
* @return <tt>true</tt> if this collection contains no elements.
*/
public boolean isEmpty() {
return !usable(header.successor());
}
/**
* Returns the number of elements in this deque. If this deque contains more
* than <tt>Integer.MAX_VALUE</tt> elements, it returns
* <tt>Integer.MAX_VALUE</tt>.
*
* <p>
* Beware that, unlike in most collections, this method is <em>NOT</em> a
* constant-time operation. Because of the asynchronous nature of these
* deques, determining the current number of elements requires traversing
* them all to count them. Additionally, it is possible for the size to
* change during execution of this method, in which case the returned result
* will be inaccurate. Thus, this method is typically not very useful in
* concurrent applications.
*
* @return the number of elements in this deque.
*/
public int size() {
long count = 0;
for (Node<E> n = header.forward(); n != null; n = n.forward())
++count;
return (count >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) count;
}
/**
* Removes the first element <tt>e</tt> such that <tt>o.equals(e)</tt>,
* if such an element exists in this deque. If the deque does not contain
* the element, it is unchanged.
*
* @param o
* element to be removed from this deque, if present.
* @return <tt>true</tt> if the deque contained the specified element.
* @throws NullPointerException
* if the specified element is <tt>null</tt>
*/
public boolean remove(Object o) {
return removeFirstOccurrence(o);
}
/**
* Appends all of the elements in the specified collection to the end of
* this deque, in the order that they are returned by the specified
* collection's iterator. The behavior of this operation is undefined if the
* specified collection is modified while the operation is in progress.
* (This implies that the behavior of this call is undefined if the
* specified Collection is this deque, and this deque is nonempty.)
*
* @param c
* the elements to be inserted into this deque.
* @return <tt>true</tt> if this deque changed as a result of the call.
* @throws NullPointerException
* if <tt>c</tt> or any element within it is <tt>null</tt>
*/
public boolean addAll(Collection<? extends E> c) {
Iterator<? extends E> it = c.iterator();
if (!it.hasNext())
return false;
do {
addLast(it.next());
} while (it.hasNext());
return true;
}
/**
* Removes all of the elements from this deque.
*/
public void clear() {
while (pollFirst() != null)
;
}
/**
* Returns an array containing all of the elements in this deque in the
* correct order.
*
* @return an array containing all of the elements in this deque in the
* correct order.
*/
public Object[] toArray() {
return toArrayList().toArray();
}
/**
* Returns an array containing all of the elements in this deque in the
* correct order; the runtime type of the returned array is that of the
* specified array. If the deque fits in the specified array, it is returned
* therein. Otherwise, a new array is allocated with the runtime type of the
* specified array and the size of this deque.
* <p>
*
* If the deque fits in the specified array with room to spare (i.e., the
* array has more elements than the deque), the element in the array
* immediately following the end of the collection is set to null. This is
* useful in determining the length of the deque <i>only</i> if the caller
* knows that the deque does not contain any null elements.
*
* @param a
* the array into which the elements of the deque are to be
* stored, if it is big enough; otherwise, a new array of the
* same runtime type is allocated for this purpose.
* @return an array containing the elements of the deque.
* @throws ArrayStoreException
* if the runtime type of a is not a supertype of the runtime
* type of every element in this deque.
* @throws NullPointerException
* if the specified array is null.
*/
public <T> T[] toArray(T[] a) {
return toArrayList().toArray(a);
}
/**
* Returns a weakly consistent iterator over the elements in this deque, in
* first-to-last order. The <tt>next</tt> method returns elements
* reflecting the state of the deque at some point at or since the creation
* of the iterator. The method does <em>not</em> throw
* {@link ConcurrentModificationException}, and may proceed concurrently
* with other operations.
*
* @return an iterator over the elements in this deque
*/
public Iterator<E> iterator() {
return new CLDIterator();
}
final class CLDIterator implements Iterator<E> {
Node<E> last;
Node<E> next = header.forward();
public boolean hasNext() {
return next != null;
}
public E next() {
Node<E> l = last = next;
if (l == null)
throw new NoSuchElementException();
next = next.forward();
return l.element;
}
public void remove() {
Node<E> l = last;
if (l == null)
throw new IllegalStateException();
while (!l.delete() && !l.isDeleted())
;
}
}
}
/**
* Linked Nodes. As a minor efficiency hack, this class opportunistically
* inherits from AtomicReference, with the atomic ref used as the "next"
* link.
*
* Nodes are in doubly-linked lists. There are three kinds of special nodes,
* distinguished by: * The list header has a null prev link * The list
* trailer has a null next link * A deletion marker has a prev link pointing
* to itself. All three kinds of special nodes have null element fields.
*
* Regular nodes have non-null element, next, and prev fields. To avoid
* visible inconsistencies when deletions overlap element replacement,
* replacements are done by replacing the node, not just setting the
* element.
*
* Nodes can be traversed by read-only ConcurrentLinkedDeque class
* operations just by following raw next pointers, so long as they ignore
* any special nodes seen along the way. (This is automated in method
* forward.) However, traversal using prev pointers is not guaranteed to see
* all live nodes since a prev pointer of a deleted node can become
* unrecoverably stale.
*/
class Node<E> extends AtomicReference<Node<E>> {
private volatile Node<E> prev;
final E element;
/** Creates a node with given contents */
Node(E element, Node<E> next, Node<E> prev) {
super(next);
this.prev = prev;
this.element = element;
}
/** Creates a marker node with given successor */
Node(Node<E> next) {
super(next);
this.prev = this;
this.element = null;
}
/**
* Gets next link (which is actually the value held as atomic
* reference).
*/
private Node<E> getNext() {
return get();
}
/**
* Sets next link
*
* @param n
* the next node
*/
void setNext(Node<E> n) {
set(n);
}
/**
* compareAndSet next link
*/
private boolean casNext(Node<E> cmp, Node<E> val) {
return compareAndSet(cmp, val);
}
/**
* Gets prev link
*/
private Node<E> getPrev() {
return prev;
}
/**
* Sets prev link
*
* @param b
* the previous node
*/
void setPrev(Node<E> b) {
prev = b;
}
/**
* Returns true if this is a header, trailer, or marker node
*/
boolean isSpecial() {
return element == null;
}
/**
* Returns true if this is a trailer node
*/
boolean isTrailer() {
return getNext() == null;
}
/**
* Returns true if this is a header node
*/
boolean isHeader() {
return getPrev() == null;
}
/**
* Returns true if this is a marker node
*/
boolean isMarker() {
return getPrev() == this;
}
/**
* Returns true if this node is followed by a marker, meaning that it is
* deleted.
*
* @return true if this node is deleted
*/
boolean isDeleted() {
Node<E> f = getNext();
return f != null && f.isMarker();
}
/**
* Returns next node, ignoring deletion marker
*/
private Node<E> nextNonmarker() {
Node<E> f = getNext();
return (f == null || !f.isMarker()) ? f : f.getNext();
}
/**
* Returns the next non-deleted node, swinging next pointer around any
* encountered deleted nodes, and also patching up successor''s prev
* link to point back to this. Returns null if this node is trailer so
* has no successor.
*
* @return successor, or null if no such
*/
Node<E> successor() {
Node<E> f = nextNonmarker();
for (;;) {
if (f == null)
return null;
if (!f.isDeleted()) {
if (f.getPrev() != this && !isDeleted())
f.setPrev(this); // relink f's prev
return f;
}
Node<E> s = f.nextNonmarker();
if (f == getNext())
casNext(f, s); // unlink f
f = s;
}
}
/**
* Returns the apparent predecessor of target by searching forward for
* it starting at this node, patching up pointers while traversing. Used
* by predecessor().
*
* @return target's predecessor, or null if not found
*/
private Node<E> findPredecessorOf(Node<E> target) {
Node<E> n = this;
for (;;) {
Node<E> f = n.successor();
if (f == target)
return n;
if (f == null)
return null;
n = f;
}
}
/**
* Returns the previous non-deleted node, patching up pointers as
* needed. Returns null if this node is header so has no successor. May
* also return null if this node is deleted, so doesn't have a distinct
* predecessor.
*
* @return predecessor or null if not found
*/
Node<E> predecessor() {
Node<E> n = this;
for (;;) {
Node<E> b = n.getPrev();
if (b == null)
return n.findPredecessorOf(this);
Node<E> s = b.getNext();
if (s == this)
return b;
if (s == null || !s.isMarker()) {
Node<E> p = b.findPredecessorOf(this);
if (p != null)
return p;
}
n = b;
}
}
/**
* Returns the next node containing a nondeleted user element. Use for
* forward list traversal.
*
* @return successor, or null if no such
*/
Node<E> forward() {
Node<E> f = successor();
return (f == null || f.isSpecial()) ? null : f;
}
/**
* Returns previous node containing a nondeleted user element, if
* possible. Use for backward list traversal, but beware that if this
* method is called from a deleted node, it might not be able to
* determine a usable predecessor.
*
* @return predecessor, or null if no such could be found
*/
Node<E> back() {
Node<E> f = predecessor();
return (f == null || f.isSpecial()) ? null : f;
}
/**
* Tries to insert a node holding element as successor, failing if this
* node is deleted.
*
* @param element
* the element
* @return the new node, or null on failure.
*/
Node<E> append(E element) {
for (;;) {
Node<E> f = getNext();
if (f == null || f.isMarker())
return null;
Node<E> x = new Node<E>(element, f, this);
if (casNext(f, x)) {
f.setPrev(x); // optimistically link
return x;
}
}
}
/**
* Tries to insert a node holding element as predecessor, failing if no
* live predecessor can be found to link to.
*
* @param element
* the element
* @return the new node, or null on failure.
*/
Node<E> prepend(E element) {
for (;;) {
Node<E> b = predecessor();
if (b == null)
return null;
Node<E> x = new Node<E>(element, this, b);
if (b.casNext(this, x)) {
setPrev(x); // optimistically link
return x;
}
}
}
/**
* Tries to mark this node as deleted, failing if already deleted or if
* this node is header or trailer
*
* @return true if successful
*/
boolean delete() {
Node<E> b = getPrev();
Node<E> f = getNext();
if (b != null && f != null && !f.isMarker()
&& casNext(f, new Node(f))) {
if (b.casNext(this, f))
f.setPrev(b);
return true;
}
return false;
}
/**
* Tries to insert a node holding element to replace this node. failing
* if already deleted.
*
* @param newElement
* the new element
* @return the new node, or null on failure.
*/
Node<E> replace(E newElement) {
for (;;) {
Node<E> b = getPrev();
Node<E> f = getNext();
if (b == null || f == null || f.isMarker())
return null;
Node<E> x = new Node<E>(newElement, f, b);
if (casNext(f, new Node(x))) {
b.successor(); // to relink b
x.successor(); // to relink f
return x;
}
}
}
}
import java.util.LinkedList;
import java.util.List;
*/
public class SynchronizedQueue
{
// Attributes ///////////////////////////////////////////////////////////////
/**
* Cache of object produced by producer and consumed by consumer.
*/
protected List m_lstObjects;
// Constructors /////////////////////////////////////////////////////////////
/**
* Constructor for Synchronized Queue Object.
*/
public SynchronizedQueue(
)
{
super();
m_lstObjects = new LinkedList();
}
// Logic ////////////////////////////////////////////////////////////////////
/**
* Destructor for Synchronized Queue. It is called when no other
* object holds reference to it.
*
* @exception Throwable - default destructor exception
*/
protected void finalize(
) throws Throwable
{
// Explicitely remove this just to help garbage collector
m_lstObjects.clear();
m_lstObjects = null;
super.finalize();
}
/**
* Get the object from the beginning of the queue
*
* @return Object - object from the queue, if the thread is blocked in this
* function and you call interrupt method, an InterruptedException
* will be thrown.
* @exception InterruptedException - if the thread is blocked in this
* function and you call interrupt method,
* an InterruptedException will be thrown.
*/
public synchronized Object get(
) throws InterruptedException
{
Object objReturn = null;
if (m_lstObjects.isEmpty())
{
// There is no object in the queue, go to sleep
try
{
wait();
}
catch (InterruptedException ieException)
{
// Somebody woke us up, that means all threads waiting on this
// object competed for the lock and this one won and the object is
// locked again
// The thread can be woken up in two conditions, producer put new
// object into the queue or somebody called interrupt - to interrupt
// the wait - in this case rethrow an exception
if (m_lstObjects.isEmpty())
{
throw ieException;
}
}
}
// Remove the first object in the queue
objReturn = m_lstObjects.remove(0);
return objReturn;
}
/**
* Put the object to the end of the queue.
*
* @param objNew - new object, can be null
*/
public synchronized void put(
Object objNew
)
{
m_lstObjects.add(objNew);
// New object in the queue, notify others
notifyAll();
}
/**
* Test if the queue is empty.
*
* @return boolean - true if the queue is empty
*/
public synchronized boolean isEmpty(
)
{
return m_lstObjects.isEmpty();
}
}
import java.io.IOException;
import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
public class ConcurrentReaderHashMap extends AbstractMap implements Map, Cloneable, Serializable
{
private static final long serialVersionUID = 1L;
/** A Serializable class for barrier lock * */
protected static class BarrierLock implements java.io.Serializable
{
private static final long serialVersionUID = 1L;
}
/**
* Lock used only for its memory effects.
*/
protected final BarrierLock barrierLock = new BarrierLock();
/**
* field written to only to guarantee lock ordering.
*/
protected transient Object lastWrite;
/**
* Force a memory synchronization that will cause all readers to see table.
* Call only when already holding main synch lock.
* @param x
*/
protected final void recordModification(Object x)
{
synchronized (barrierLock)
{
lastWrite = x;
}
}
/**
* Get ref to table; the reference and the cells it accesses will be at
* least as fresh as from last use of barrierLock
* @return table cells
*/
protected final Entry[] getTableForReading()
{
synchronized (barrierLock)
{
return table;
}
}
/**
* The default initial number of table slots for this table (32). Used when
* not otherwise specified in constructor.
*/
public static final int DEFAULT_INITIAL_CAPACITY = 32;
/**
* The minimum capacity, used if a lower value is implicitly specified by
* either of the constructors with arguments. MUST be a power of two.
*/
private static final int MINIMUM_CAPACITY = 4;
/**
* The maximum capacity, used if a higher value is implicitly specified by
* either of the constructors with arguments. MUST be a power of two <= 1<<30.
*/
private static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* The default load factor for this table (1.0). Used when not otherwise
* specified in constructor.
*/
public static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* The hash table data.
*/
protected transient Entry[] table;
/**
* The total number of mappings in the hash table.
*/
protected transient int count;
/**
* The table is rehashed when its size exceeds this threshold. (The value of
* this field is always (int)(capacity * loadFactor).)
*
* @serial
*/
protected int threshold;
/**
* The load factor for the hash table.
*
* @serial
*/
protected float loadFactor;
/**
* Returns the appropriate capacity (power of two) for the specified initial
* capacity argument.
* @param initialCapacity
* @return appropriate capacity
*/
private int p2capacity(int initialCapacity)
{
int cap = initialCapacity;
// Compute the appropriate capacity
int result;
if (cap > MAXIMUM_CAPACITY || cap < 0)
{
result = MAXIMUM_CAPACITY;
}
else
{
result = MINIMUM_CAPACITY;
while (result < cap)
{
result <<= 1;
}
}
return result;
}
/**
* Return hash code for Object x. Since we are using power-of-two tables, it
* is worth the effort to improve hashcode via the same multiplicative
* scheme as used in IdentityHashMap.
* @param x
* @return hash code
*/
private static int hash(Object x)
{
int h = x.hashCode();
// Multiply by 127 (quickly, via shifts), and mix in some high
// bits to help guard against bunching of codes that are
// consecutive or equally spaced.
return ((h << 7) - h + (h >>> 9) + (h >>> 17));
}
/**
* Check for equality of non-null references x and y.
* @param x
* @param y
* @return equality
*/
protected boolean eq(Object x, Object y)
{
return x == y || x.equals(y);
}
/**
* Constructs a new, empty map with the specified initial capacity and the
* specified load factor.
*
* @param initialCapacity
* the initial capacity The actual initial capacity is rounded to
* the nearest power of two.
* @param loadFactor
* the load factor of the ConcurrentReaderHashMap
* @throws IllegalArgumentException
* if the initial maximum number of elements is less than zero,
* or if the load factor is nonpositive.
*/
public ConcurrentReaderHashMap(int initialCapacity, float loadFactor)
{
if (loadFactor <= 0)
{
throw new IllegalArgumentException("Illegal Load factor: " + loadFactor);
}
this.loadFactor = loadFactor;
int cap = p2capacity(initialCapacity);
table = new Entry[cap];
threshold = (int)(cap * loadFactor);
}
/**
* Constructs a new, empty map with the specified initial capacity and
* default load factor.
*
* @param initialCapacity
* the initial capacity of the ConcurrentReaderHashMap.
* @throws IllegalArgumentException
* if the initial maximum number of elements is less than zero.
*/
public ConcurrentReaderHashMap(int initialCapacity)
{
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/**
* Constructs a new, empty map with a default initial capacity and load
* factor.
*/
public ConcurrentReaderHashMap()
{
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
/**
* Constructs a new map with the same mappings as the given map. The map is
* created with a capacity of twice the number of mappings in the given map
* or 16 (whichever is greater), and a default load factor.
* @param t
*/
public ConcurrentReaderHashMap(Map t)
{
this(Math.max((int)(t.size() / DEFAULT_LOAD_FACTOR) + 1, 16), DEFAULT_LOAD_FACTOR);
putAll(t);
}
/**
* Returns the number of key-value mappings in this map.
*
* @return the number of key-value mappings in this map.
*/
public synchronized int size()
{
return count;
}
/**
* Returns <tt>true</tt> if this map contains no key-value mappings.
*
* @return <tt>true</tt> if this map contains no key-value mappings.
*/
public synchronized boolean isEmpty()
{
return count == 0;
}
/**
* Returns the value to which the specified key is mapped in this table.
*
* @param key
* a key in the table.
* @return the value to which the key is mapped in this table;
* <code>null</code> if the key is not mapped to any value in this
* table.
* @exception NullPointerException
* if the key is <code>null</code>.
* @see #put(Object, Object)
*/
public Object get(Object key)
{
// throw null pointer exception if key null
int hash = hash(key);
/*
* Start off at the apparently correct bin. If entry is found, we need
* to check after a barrier anyway. If not found, we need a barrier to
* check if we are actually in right bin. So either way, we encounter
* only one barrier unless we need to retry. And we only need to fully
* synchronize if there have been concurrent modifications.
*/
Entry[] tab = table;
int index = hash & (tab.length - 1);
Entry first = tab[index];
Entry e = first;
for (;;)
{
if (e == null)
{
// If key apparently not there, check to
// make sure this was a valid read
Entry[] reread = getTableForReading();
if (tab == reread && first == tab[index])
{
return null;
}
else
{
// Wrong list -- must restart traversal at new first
tab = reread;
e = first = tab[index = hash & (tab.length - 1)];
}
}
else if (e.hash == hash && eq(key, e.key))
{
Object value = e.value;
if (value != null)
{
return value;
}
// Entry was invalidated during deletion. But it could
// have been re-inserted, so we must retraverse.
// To avoid useless contention, get lock to wait out
// modifications
// before retraversing.
synchronized (this)
{
tab = table;
}
e = first = tab[index = hash & (tab.length - 1)];
}
else
{
e = e.next;
}
}
}
/**
* Tests if the specified object is a key in this table.
*
* @param key
* possible key.
* @return <code>true</code> if and only if the specified object is a key
* in this table, as determined by the <tt>equals</tt> method;
* <code>false</code> otherwise.
* @exception NullPointerException
* if the key is <code>null</code>.
* @see #contains(Object)
*/
public boolean containsKey(Object key)
{
return get(key) != null;
}
/**
* Maps the specified <code>key</code> to the specified <code>value</code>
* in this table. Neither the key nor the value can be <code>null</code>.
* <p>
*
* The value can be retrieved by calling the <code>get</code> method with
* a key that is equal to the original key.
*
* @param key
* the table key.
* @param value
* the value.
* @return the previous value of the specified key in this table, or
* <code>null</code> if it did not have one.
* @exception NullPointerException
* if the key or value is <code>null</code>.
* @see Object#equals(Object)
* @see #get(Object)
*/
public Object put(Object key, Object value)
{
if (value == null)
{
throw new IllegalArgumentException("Value must not be null");
}
int hash = hash(key);
Entry[] tab = table;
int index = hash & (tab.length - 1);
Entry first = tab[index];
Entry e;
for (e = first; e != null; e = e.next)
{
if (e.hash == hash && eq(key, e.key))
{
break;
}
}
synchronized (this)
{
if (tab == table)
{
if (e == null)
{
// make sure we are adding to correct list
if (first == tab[index])
{
// Add to front of list
Entry newEntry = new Entry(hash, key, value, first);
tab[index] = newEntry;
if (++count >= threshold)
{
rehash();
}
else
{
recordModification(newEntry);
}
return null;
}
}
else
{
Object oldValue = e.value;
if (first == tab[index] && oldValue != null)
{
e.value = value;
return oldValue;
}
}
}
// retry if wrong list or lost race against concurrent remove
return sput(key, value, hash);
}
}
/**
* Continuation of put(), called only when synch lock is held and
* interference has been detected.
* @param key
* @param value
* @param hash
* @return continuation object
*/
protected Object sput(Object key, Object value, int hash)
{
Entry[] tab = table;
int index = hash & (tab.length - 1);
Entry first = tab[index];
Entry e = first;
for (;;)
{
if (e == null)
{
Entry newEntry = new Entry(hash, key, value, first);
tab[index] = newEntry;
if (++count >= threshold)
{
rehash();
}
else
{
recordModification(newEntry);
}
return null;
}
else if (e.hash == hash && eq(key, e.key))
{
Object oldValue = e.value;
e.value = value;
return oldValue;
}
else
{
e = e.next;
}
}
}
/**
* Rehashes the contents of this map into a new table with a larger
* capacity. This method is called automatically when the number of keys in
* this map exceeds its capacity and load factor.
*/
protected void rehash()
{
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity >= MAXIMUM_CAPACITY)
{
threshold = Integer.MAX_VALUE; // avoid retriggering
return;
}
int newCapacity = oldCapacity << 1;
int mask = newCapacity - 1;
threshold = (int)(newCapacity * loadFactor);
Entry[] newTable = new Entry[newCapacity];
/*
* Reclassify nodes in each list to new Map. Because we are using
* power-of-two expansion, the elements from each bin must either stay
* at same index, or move to oldCapacity+index. We also eliminate
* unnecessary node creation by catching cases where old nodes can be
* reused because their next fields won't change. Statistically, at the
* default threshhold, only about one-sixth of them need cloning. (The
* nodes they replace will be garbage collectable as soon as they are no
* longer referenced by any reader thread that may be in the midst of
* traversing table right now.)
*/
for (int i = 0; i < oldCapacity; i++)
{
// We need to guarantee that any existing reads of old Map can
// proceed. So we cannot yet null out each bin.
Entry e = oldTable[i];
if (e != null)
{
int idx = e.hash & mask;
Entry next = e.next;
// Single node on list
if (next == null)
{
newTable[idx] = e;
}
else
{
// Reuse trailing consecutive sequence of all same bit
Entry lastRun = e;
int lastIdx = idx;
for (Entry last = next; last != null; last = last.next)
{
int k = last.hash & mask;
if (k != lastIdx)
{
lastIdx = k;
lastRun = last;
}
}
newTable[lastIdx] = lastRun;
// Clone all remaining nodes
for (Entry p = e; p != lastRun; p = p.next)
{
int k = p.hash & mask;
newTable[k] = new Entry(p.hash, p.key, p.value, newTable[k]);
}
}
}
}
table = newTable;
recordModification(newTable);
}
/**
* Removes the key (and its corresponding value) from this table. This
* method does nothing if the key is not in the table.
*
* @param key
* the key that needs to be removed.
* @return the value to which the key had been mapped in this table, or
* <code>null</code> if the key did not have a mapping.
* @exception NullPointerException
* if the key is <code>null</code>.
*/
public Object remove(Object key)
{
/*
* Find the entry, then 1. Set value field to null, to force get() to
* retry 2. Rebuild the list without this entry. All entries following
* removed node can stay in list, but all preceeding ones need to be
* cloned. Traversals rely on this strategy to ensure that elements will
* not be repeated during iteration.
*/
int hash = hash(key);
Entry[] tab = table;
int index = hash & (tab.length - 1);
Entry first = tab[index];
Entry e = first;
for (e = first; e != null; e = e.next)
{
if (e.hash == hash && eq(key, e.key))
{
break;
}
}
synchronized (this)
{
if (tab == table)
{
if (e == null)
{
if (first == tab[index])
{
return null;
}
}
else
{
Object oldValue = e.value;
if (first == tab[index] && oldValue != null)
{
e.value = null;
count--;
Entry head = e.next;
for (Entry p = first; p != e; p = p.next)
{
head = new Entry(p.hash, p.key, p.value, head);
}
tab[index] = head;
recordModification(head);
return oldValue;
}
}
}
// Wrong list or interference
return sremove(key, hash);
}
}
/**
* Continuation of remove(), called only when synch lock is held and
* interference has been detected.
* @param key
* @param hash
* @return continuation object
*/
protected Object sremove(Object key, int hash)
{
Entry[] tab = table;
int index = hash & (tab.length - 1);
Entry first = tab[index];
for (Entry e = first; e != null; e = e.next)
{
if (e.hash == hash && eq(key, e.key))
{
Object oldValue = e.value;
e.value = null;
count--;
Entry head = e.next;
for (Entry p = first; p != e; p = p.next)
{
head = new Entry(p.hash, p.key, p.value, head);
}
tab[index] = head;
recordModification(head);
return oldValue;
}
}
return null;
}
/**
* Returns <tt>true</tt> if this map maps one or more keys to the
* specified value. Note: This method requires a full internal traversal of
* the hash table, and so is much slower than method <tt>containsKey</tt>.
*
* @param value
* value whose presence in this map is to be tested.
* @return <tt>true</tt> if this map maps one or more keys to the
* specified value.
* @exception NullPointerException
* if the value is <code>null</code>.
*/
public boolean containsValue(Object value)
{
if (value == null)
{
throw new IllegalArgumentException("Value must not be null");
}
Entry tab[] = getTableForReading();
for (int i = 0; i < tab.length; ++i)
{
for (Entry e = tab[i]; e != null; e = e.next)
{
if (value.equals(e.value))
{
return true;
}
}
}
return false;
}
/**
* Tests if some key maps into the specified value in this table. This
* operation is more expensive than the <code>containsKey</code> method.
* <p>
*
* Note that this method is identical in functionality to containsValue,
* (which is part of the Map interface in the collections framework).
*
* @param value
* a value to search for.
* @return <code>true</code> if and only if some key maps to the
* <code>value</code> argument in this table as determined by the
* <tt>equals</tt> method; <code>false</code> otherwise.
* @exception NullPointerException
* if the value is <code>null</code>.
* @see #containsKey(Object)
* @see #containsValue(Object)
* @see Map
*/
public boolean contains(Object value)
{
return containsValue(value);
}
/**
* Copies all of the mappings from the specified map to this one.
*
* These mappings replace any mappings that this map had for any of the keys
* currently in the specified Map.
*
* @param t
* Mappings to be stored in this map.
*/
public synchronized void putAll(Map t)
{
int n = t.size();
if (n == 0)
{
return;
}
// Expand enough to hold at least n elements without resizing.
// We can only resize table by factor of two at a time.
// It is faster to rehash with fewer elements, so do it now.
while (n >= threshold)
{
rehash();
}
for (Iterator it = t.entrySet().iterator(); it.hasNext();)
{
Map.Entry entry = (Map.Entry)it.next();
Object key = entry.getKey();
Object value = entry.getValue();
put(key, value);
}
}
/**
* Removes all mappings from this map.
*/
public synchronized void clear()
{
Entry tab[] = table;
for (int i = 0; i < tab.length; ++i)
{
// must invalidate all to force concurrent get's to wait and then
// retry
for (Entry e = tab[i]; e != null; e = e.next)
{
e.value = null;
}
tab[i] = null;
}
count = 0;
recordModification(tab);
}
/**
* Returns a shallow copy of this <tt>ConcurrentReaderHashMap</tt>
* instance: the keys and values themselves are not cloned.
*
* @return a shallow copy of this map.
*/
public synchronized Object clone() throws CloneNotSupportedException
{
try
{
ConcurrentReaderHashMap t = (ConcurrentReaderHashMap)super.clone();
t.keySet = null;
t.entrySet = null;
t.values = null;
Entry[] tab = table;
t.table = new Entry[tab.length];
Entry[] ttab = t.table;
for (int i = 0; i < tab.length; ++i)
{
Entry first = null;
for (Entry e = tab[i]; e != null; e = e.next)
{
first = new Entry(e.hash, e.key, e.value, first);
}
ttab[i] = first;
}
return t;
}
catch (CloneNotSupportedException e)
{
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
// Views
protected transient Set keySet = null;
protected transient Set entrySet = null;
protected transient Collection values = null;
/**
* Returns a set view of the keys contained in this map. The set is backed
* by the map, so changes to the map are reflected in the set, and
* vice-versa. The set supports element removal, which removes the
* corresponding mapping from this map, via the <tt>Iterator.remove</tt>,
* <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and
* <tt>clear</tt> operations. It does not support the <tt>add</tt> or
* <tt>addAll</tt> operations.
*
* @return a set view of the keys contained in this map.
*/
public Set keySet()
{
Set ks = keySet;
return (ks != null) ? ks : (keySet = new KeySet());
}
private class KeySet extends AbstractSet
{
/**
* @see Collection#iterator()
*/
public Iterator iterator()
{
return new KeyIterator();
}
/**
* @see Collection#size()
*/
public int size()
{
return ConcurrentReaderHashMap.this.size();
}
/**
* @see Collection#contains(java.lang.Object)
*/
public boolean contains(Object o)
{
return ConcurrentReaderHashMap.this.containsKey(o);
}
/**
* @see Collection#remove(java.lang.Object)
*/
public boolean remove(Object o)
{
return ConcurrentReaderHashMap.this.remove(o) != null;
}
/**
* @see Collection#clear()
*/
public void clear()
{
ConcurrentReaderHashMap.this.clear();
}
}
/**
* Returns a collection view of the values contained in this map. The
* collection is backed by the map, so changes to the map are reflected in
* the collection, and vice-versa. The collection supports element removal,
* which removes the corresponding mapping from this map, via the
* <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
* <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
* operations. It does not support the <tt>add</tt> or <tt>addAll</tt>
* operations.
*
* @return a collection view of the values contained in this map.
*/
public Collection values()
{
Collection vs = values;
return (vs != null) ? vs : (values = new Values());
}
private class Values extends AbstractCollection
{
/**
* @see Collection#iterator()
*/
public Iterator iterator()
{
return new ValueIterator();
}
/**
* @see Collection#size()
*/
public int size()
{
return ConcurrentReaderHashMap.this.size();
}
/**
* @see Collection#contains(java.lang.Object)
*/
public boolean contains(Object o)
{
return ConcurrentReaderHashMap.this.containsValue(o);
}
/**
* @see Collection#clear()
*/
public void clear()
{
ConcurrentReaderHashMap.this.clear();
}
}
/**
* Returns a collection view of the mappings contained in this map. Each
* element in the returned collection is a <tt>Map.Entry</tt>. The
* collection is backed by the map, so changes to the map are reflected in
* the collection, and vice-versa. The collection supports element removal,
* which removes the corresponding mapping from the map, via the
* <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
* <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
* operations. It does not support the <tt>add</tt> or <tt>addAll</tt>
* operations.
*
* @return a collection view of the mappings contained in this map.
*/
public Set entrySet()
{
Set es = entrySet;
return (es != null) ? es : (entrySet = new EntrySet());
}
private class EntrySet extends AbstractSet
{
/**
* @see Collection#iterator()
*/
public Iterator iterator()
{
return new HashIterator();
}
/**
* @see Collection#contains(java.lang.Object)
*/
public boolean contains(Object o)
{
if (!(o instanceof Map.Entry))
{
return false;
}
Map.Entry entry = (Map.Entry)o;
Object v = ConcurrentReaderHashMap.this.get(entry.getKey());
return v != null && v.equals(entry.getValue());
}
/**
* @see Collection#remove(java.lang.Object)
*/
public boolean remove(Object o)
{
if (!(o instanceof Map.Entry))
{
return false;
}
return ConcurrentReaderHashMap.this.findAndRemoveEntry((Map.Entry)o);
}
/**
* @see Collection#size()
*/
public int size()
{
return ConcurrentReaderHashMap.this.size();
}
/**
* @see Collection#clear()
*/
public void clear()
{
ConcurrentReaderHashMap.this.clear();
}
}
/**
* Helper method for entrySet.remove
*
* @param entry
*
* @return <code>true</code> when the element was found and removed.
*/
protected synchronized boolean findAndRemoveEntry(Map.Entry entry)
{
Object key = entry.getKey();
Object v = get(key);
if (v != null && v.equals(entry.getValue()))
{
remove(key);
return true;
}
else
{
return false;
}
}
/**
* Returns an enumeration of the keys in this table.
*
* @return an enumeration of the keys in this table.
* @see Enumeration
* @see #elements()
* @see #keySet()
* @see Map
*/
public Enumeration keys()
{
return new KeyIterator();
}
/**
* Returns an enumeration of the values in this table. Use the Enumeration
* methods on the returned object to fetch the elements sequentially.
*
* @return an enumeration of the values in this table.
* @see java.util.Enumeration
* @see #keys()
* @see #values()
* @see Map
*/
public Enumeration elements()
{
return new ValueIterator();
}
/**
* ConcurrentReaderHashMap collision list entry.
*/
protected static class Entry implements Map.Entry
{
/*
* The use of volatile for value field ensures that we can detect status
* changes without synchronization. The other fields are never changed,
* and are marked as final.
*/
protected final int hash;
protected final Object key;
protected final Entry next;
protected volatile Object value;
Entry(int hash, Object key, Object value, Entry next)
{
this.hash = hash;
this.key = key;
this.next = next;
this.value = value;
}
// Map.Entry Ops
/**
* @see Map.Entry#getKey()
*/
public Object getKey()
{
return key;
}
/**
* Get the value. Note: In an entrySet or entrySet.iterator, unless the
* set or iterator is used under synchronization of the table as a whole
* (or you can otherwise guarantee lack of concurrent modification),
* <tt>getValue</tt> <em>might</em> return null, reflecting the fact
* that the entry has been concurrently removed. However, there are no
* assurances that concurrent removals will be reflected using this
* method.
*
* @return the current value, or null if the entry has been detectably
* removed.
*/
public Object getValue()
{
return value;
}
/**
* Set the value of this entry. Note: In an entrySet or
* entrySet.iterator), unless the set or iterator is used under
* synchronization of the table as a whole (or you can otherwise
* guarantee lack of concurrent modification), <tt>setValue</tt> is
* not strictly guaranteed to actually replace the value field obtained
* via the <tt>get</tt> operation of the underlying hash table in
* multithreaded applications. If iterator-wide synchronization is not
* used, and any other concurrent <tt>put</tt> or <tt>remove</tt>
* operations occur, sometimes even to <em>other</em> entries, then
* this change is not guaranteed to be reflected in the hash table. (It
* might, or it might not. There are no assurances either way.)
*
* @param value
* the new value.
* @return the previous value, or null if entry has been detectably
* removed.
* @exception NullPointerException
* if the value is <code>null</code>.
*
*/
public Object setValue(Object value)
{
if (value == null)
{
throw new IllegalArgumentException("Value must not be null");
}
Object oldValue = this.value;
this.value = value;
return oldValue;
}
/**
* @see Object#equals(java.lang.Object)
*/
public boolean equals(Object o)
{
if (!(o instanceof Map.Entry))
{
return false;
}
Map.Entry e = (Map.Entry)o;
return (key.equals(e.getKey()) && value.equals(e.getValue()));
}
/**
* @see Object#hashCode()
*/
public int hashCode()
{
return key.hashCode() ^ value.hashCode();
}
/**
* @see Object#toString()
*/
public String toString()
{
return key + "=" + value;
}
}
protected class HashIterator implements Iterator, Enumeration
{
protected final Entry[] tab; // snapshot of table
protected int index; // current slot
protected Entry entry = null; // current node of slot
protected Object currentKey; // key for current node
protected Object currentValue; // value for current node
protected Entry lastReturned = null; // last node returned by next
protected HashIterator()
{
tab = ConcurrentReaderHashMap.this.getTableForReading();
index = tab.length - 1;
}
/**
* @see Enumeration#hasMoreElements()
*/
public boolean hasMoreElements()
{
return hasNext();
}
/**
* @see Enumeration#nextElement()
*/
public Object nextElement()
{
return next();
}
/**
* @see Iterator#hasNext()
*/
public boolean hasNext()
{
/*
* currentkey and currentValue are set here to ensure that next()
* returns normally if hasNext() returns true. This avoids surprises
* especially when final element is removed during traversal --
* instead, we just ignore the removal during current traversal.
*/
for (;;)
{
if (entry != null)
{
Object v = entry.value;
if (v != null)
{
currentKey = entry.key;
currentValue = v;
return true;
}
else
{
entry = entry.next;
}
}
while (entry == null && index >= 0)
{
entry = tab[index--];
}
if (entry == null)
{
currentKey = currentValue = null;
return false;
}
}
}
protected Object returnValueOfNext()
{
return entry;
}
/**
* @see Iterator#next()
*/
public Object next()
{
if (currentKey == null && !hasNext())
{
throw new NoSuchElementException();
}
Object result = returnValueOfNext();
lastReturned = entry;
currentKey = currentValue = null;
entry = entry.next;
return result;
}
/**
* @see Iterator#remove()
*/
public void remove()
{
if (lastReturned == null)
{
throw new IllegalStateException();
}
ConcurrentReaderHashMap.this.remove(lastReturned.key);
lastReturned = null;
}
}
protected class KeyIterator extends HashIterator
{
protected Object returnValueOfNext()
{
return currentKey;
}
}
protected class ValueIterator extends HashIterator
{
protected Object returnValueOfNext()
{
return currentValue;
}
}
/**
* Save the state of the <tt>ConcurrentReaderHashMap</tt> instance to a
* stream (i.e., serialize it).
* @param s
* @throws IOException
*
* @serialData The <i>capacity</i> of the ConcurrentReaderHashMap (the
* length of the bucket array) is emitted (int), followed by the
* <i>size</i> of the ConcurrentReaderHashMap (the number of
* key-value mappings), followed by the key (Object) and value
* (Object) for each key-value mapping represented by the
* ConcurrentReaderHashMap The key-value mappings are emitted in
* no particular order.
*/
private synchronized void writeObject(java.io.ObjectOutputStream s) throws IOException
{
// Write out the threshold, loadfactor, and any hidden stuff
s.defaultWriteObject();
// Write out number of buckets
s.writeInt(table.length);
// Write out size (number of Mappings)
s.writeInt(count);
// Write out keys and values (alternating)
for (int index = table.length - 1; index >= 0; index--)
{
Entry entry = table[index];
while (entry != null)
{
s.writeObject(entry.key);
s.writeObject(entry.value);
entry = entry.next;
}
}
}
/**
* Reconstitute the <tt>ConcurrentReaderHashMap</tt> instance from a
* stream (i.e., deserialize it).
* @param s
* @throws IOException
* @throws ClassNotFoundException
*/
private synchronized void readObject(java.io.ObjectInputStream s) throws IOException,
ClassNotFoundException
{
// Read in the threshold, loadfactor, and any hidden stuff
s.defaultReadObject();
// Read in number of buckets and allocate the bucket array;
int numBuckets = s.readInt();
table = new Entry[numBuckets];
// Read in size (number of Mappings)
int size = s.readInt();
// Read the keys and values, and put the mappings in the table
for (int i = 0; i < size; i++)
{
Object key = s.readObject();
Object value = s.readObject();
put(key, value);
}
}
/**
* Return the number of slots in this table
* @return number of slots in this table
*/
public synchronized int capacity()
{
return table.length;
}
/**
* Return the load factor
* @return the load factor
*/
public float loadFactor()
{
return loadFactor;
}
}
import java.io.IOException;
import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
public class ConcurrentHashMap extends AbstractMap implements Map, Cloneable, Serializable
{
private static final long serialVersionUID = 1L;
/**
* The hash table data.
*/
protected transient Entry[] table;
/**
* The number of concurrency control segments. The value can be at most 32
* since ints are used as bitsets over segments. Emprically, it doesn't seem
* to pay to decrease it either, so the value should be at least 32. In
* other words, do not redefine this :-)
*/
protected static final int CONCURRENCY_LEVEL = 32;
/**
* Mask value for indexing into segments
*/
protected static final int SEGMENT_MASK = CONCURRENCY_LEVEL - 1;
/**
* Bookkeeping for each concurrency control segment. Each segment contains a
* local count of the number of elements in its region. However, the main
* use of a Segment is for its lock.
*/
protected final static class Segment implements Serializable
{
private static final long serialVersionUID = 1L;
/**
* The number of elements in this segment's region. It is always updated
* within synchronized blocks.
*/
protected int count;
/**
* Get the count under synch.
* @return count under sync
*/
protected synchronized int getCount()
{
return count;
}
/**
* Force a synchronization
*/
protected synchronized void synch()
{
}
}
/**
* The array of concurrency control segments.
*/
protected final Segment[] segments = new Segment[CONCURRENCY_LEVEL];
/**
* The default initial number of table slots for this table (32). Used when
* not otherwise specified in constructor.
*/
public static final int DEFAULT_INITIAL_CAPACITY = 32;
/**
* The minimum capacity, used if a lower value is implicitly specified by
* either of the constructors with arguments. MUST be a power of two.
*/
private static final int MINIMUM_CAPACITY = 32;
/**
* The maximum capacity, used if a higher value is implicitly specified by
* either of the constructors with arguments. MUST be a power of two <= 1<<30.
*/
private static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* The default load factor for this table (0.75) Used when not otherwise
* specified in constructor.
*/
public static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* The load factor for the hash table.
*
* @serial
*/
protected final float loadFactor;
/**
* Per-segment resize threshold.
*
* @serial
*/
protected int threshold;
/**
* Number of segments voting for resize. The table is doubled when 1/4 of
* the segments reach threshold. Volatile but updated without synch since
* this is just a heuristic.
*/
protected transient volatile int votesForResize;
/**
* Return the number of set bits in w. For a derivation of this algorithm,
* see "Algorithms and data structures with applications to graphics and
* geometry", by Jurg Nievergelt and Klaus Hinrichs, Prentice Hall, 1993.
* See also notes by Torsten Sillke at
* http://www.mathematik.uni-bielefeld.de/~sillke/PROBLEMS/bitcount
* @param w arg
* @return number of set bits
*/
protected static int bitcount(int w)
{
w -= (0xaaaaaaaa & w) >>> 1;
w = (w & 0x33333333) + ((w >>> 2) & 0x33333333);
w = (w + (w >>> 4)) & 0x0f0f0f0f;
w += w >>> 8;
w += w >>> 16;
return w & 0xff;
}
/**
* Returns the appropriate capacity (power of two) for the specified initial
* capacity argument.
* @param initialCapacity the initial capacity
* @return appropriate capacity
*/
private int p2capacity(int initialCapacity)
{
int cap = initialCapacity;
// Compute the appropriate capacity
int result;
if (cap > MAXIMUM_CAPACITY || cap < 0)
{
result = MAXIMUM_CAPACITY;
}
else
{
result = MINIMUM_CAPACITY;
while (result < cap)
{
result <<= 1;
}
}
return result;
}
/**
* Return hash code for Object x. Since we are using power-of-two tables, it
* is worth the effort to improve hashcode via the same multiplicative
* scheme as used in IdentityHashMap.
* @param x
* @return hash code
*/
protected static int hash(Object x)
{
int h = x.hashCode();
// Multiply by 127 (quickly, via shifts), and mix in some high
// bits to help guard against bunching of codes that are
// consecutive or equally spaced.
return ((h << 7) - h + (h >>> 9) + (h >>> 17));
}
/**
* Check for equality of non-null references x and y.
* @param x ref
* @param y ref
* @return is equal
*/
protected boolean eq(Object x, Object y)
{
return x == y || x.equals(y);
}
/**
* Create table array and set the per-segment threshold *
* @param capacity
* @return table array
*/
protected Entry[] newTable(int capacity)
{
threshold = (int)(capacity * loadFactor / CONCURRENCY_LEVEL) + 1;
return new Entry[capacity];
}
/**
* Constructs a new, empty map with the specified initial capacity and the
* specified load factor.
*
* @param initialCapacity
* the initial capacity. The actual initial capacity is rounded
* to the nearest power of two.
* @param loadFactor
* the load factor threshold, used to control resizing. This
* value is used in an approximate way: When at least a quarter
* of the segments of the table reach per-segment threshold, or
* one of the segments itself exceeds overall threshold, the
* table is doubled. This will on average cause resizing when the
* table-wide load factor is slightly less than the threshold. If
* you'd like to avoid resizing, you can set this to a
* ridiculously large value.
* @throws IllegalArgumentException
* if the load factor is nonpositive.
*/
public ConcurrentHashMap(int initialCapacity, float loadFactor)
{
if (!(loadFactor > 0))
{
throw new IllegalArgumentException("Illegal Load factor: " + loadFactor);
}
this.loadFactor = loadFactor;
for (int i = 0; i < segments.length; ++i)
{
segments[i] = new Segment();
}
int cap = p2capacity(initialCapacity);
table = newTable(cap);
}
/**
* Constructs a new, empty map with the specified initial capacity and
* default load factor.
*
* @param initialCapacity
* the initial capacity of the ConcurrentHashMap.
* @throws IllegalArgumentException
* if the initial maximum number of elements is less than zero.
*/
public ConcurrentHashMap(int initialCapacity)
{
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/**
* Constructs a new, empty map with a default initial capacity and default
* load factor.
*/
public ConcurrentHashMap()
{
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
/**
* Constructs a new map with the same mappings as the given map. The map is
* created with a capacity of twice the number of mappings in the given map
* or 32 (whichever is greater), and a default load factor.
* @param t map to copy
*/
public ConcurrentHashMap(Map t)
{
this(Math.max((int)(t.size() / DEFAULT_LOAD_FACTOR) + 1, MINIMUM_CAPACITY),
DEFAULT_LOAD_FACTOR);
putAll(t);
}
/**
* Returns the number of key-value mappings in this map.
*
* @return the number of key-value mappings in this map.
*/
public int size()
{
int c = 0;
for (int i = 0; i < segments.length; ++i)
{
c += segments[i].getCount();
}
return c;
}
/**
* Returns <tt>true</tt> if this map contains no key-value mappings.
*
* @return <tt>true</tt> if this map contains no key-value mappings.
*/
public boolean isEmpty()
{
for (int i = 0; i < segments.length; ++i)
{
if (segments[i].getCount() != 0)
{
return false;
}
}
return true;
}
/**
* Returns the value to which the specified key is mapped in this table.
*
* @param key
* a key in the table.
* @return the value to which the key is mapped in this table;
* <code>null</code> if the key is not mapped to any value in this
* table.
* @exception NullPointerException
* if the key is <code>null</code>.
* @see #put(Object, Object)
*/
public Object get(Object key)
{
int hash = hash(key); // throws null pointer exception if key null
// Try first without locking...
Entry[] tab = table;
int index = hash & (tab.length - 1);
Entry first = tab[index];
Entry e;
for (e = first; e != null; e = e.next)
{
if (e.hash == hash && eq(key, e.key))
{
Object value = e.value;
if (value != null)
{
return value;
}
else
{
break;
}
}
}
// Recheck under synch if key apparently not there or interference
Segment seg = segments[hash & SEGMENT_MASK];
synchronized (seg)
{
tab = table;
index = hash & (tab.length - 1);
Entry newFirst = tab[index];
if (e != null || first != newFirst)
{
for (e = newFirst; e != null; e = e.next)
{
if (e.hash == hash && eq(key, e.key))
{
return e.value;
}
}
}
return null;
}
}
/**
* Tests if the specified object is a key in this table.
*
* @param key
* possible key.
* @return <code>true</code> if and only if the specified object is a key
* in this table, as determined by the <tt>equals</tt> method;
* <code>false</code> otherwise.
* @exception NullPointerException
* if the key is <code>null</code>.
* @see #contains(Object)
*/
public boolean containsKey(Object key)
{
return get(key) != null;
}
/**
* Maps the specified <code>key</code> to the specified <code>value</code>
* in this table. Neither the key nor the value can be <code>null</code>.
* (Note that this policy is the same as for java.util.Hashtable, but unlike
* java.util.HashMap, which does accept nulls as valid keys and values.)
* <p>
*
* The value can be retrieved by calling the <code>get</code> method with
* a key that is equal to the original key.
*
* @param key
* the table key.
* @param value
* the value.
* @return the previous value of the specified key in this table, or
* <code>null</code> if it did not have one.
* @exception NullPointerException
* if the key or value is <code>null</code>.
* @see Object#equals(Object)
* @see #get(Object)
*/
public Object put(Object key, Object value)
{
if (value == null)
{
throw new IllegalArgumentException("Value must not be null");
}
int hash = hash(key);
Segment seg = segments[hash & SEGMENT_MASK];
int segcount;
Entry[] tab;
int votes;
synchronized (seg)
{
tab = table;
int index = hash & (tab.length - 1);
Entry first = tab[index];
for (Entry e = first; e != null; e = e.next)
{
if (e.hash == hash && eq(key, e.key))
{
Object oldValue = e.value;
e.value = value;
return oldValue;
}
}
// Add to front of list
Entry newEntry = new Entry(hash, key, value, first);
tab[index] = newEntry;
if ((segcount = ++seg.count) < threshold)
{
return null;
}
int bit = (1 << (hash & SEGMENT_MASK));
votes = votesForResize;
if ((votes & bit) == 0)
{
votes = votesForResize |= bit;
}
}
// Attempt resize if 1/4 segs vote,
// or if this seg itself reaches the overall threshold.
// (The latter check is just a safeguard to avoid pathological cases.)
if (bitcount(votes) >= CONCURRENCY_LEVEL / 4 || segcount > (threshold * CONCURRENCY_LEVEL))
{
resize(0, tab);
}
return null;
}
/**
* Gather all locks in order to call rehash, by recursing within synch
* blocks for each segment index.
*
* @param index
* the current segment. initially call value must be 0
* @param assumedTab
* the state of table on first call to resize. If this changes on
* any call, the attempt is aborted because the table has already
* been resized by another thread.
*/
protected void resize(int index, Entry[] assumedTab)
{
Segment seg = segments[index];
synchronized (seg)
{
if (assumedTab == table)
{
int next = index + 1;
if (next < segments.length)
{
resize(next, assumedTab);
}
else
{
rehash();
}
}
}
}
/**
* Rehashes the contents of this map into a new table with a larger
* capacity.
*/
protected void rehash()
{
votesForResize = 0; // reset
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity >= MAXIMUM_CAPACITY)
{
threshold = Integer.MAX_VALUE; // avoid retriggering
return;
}
int newCapacity = oldCapacity << 1;
Entry[] newTable = newTable(newCapacity);
int mask = newCapacity - 1;
/*
* Reclassify nodes in each list to new Map. Because we are using
* power-of-two expansion, the elements from each bin must either stay
* at same index, or move to oldCapacity+index. We also eliminate
* unnecessary node creation by catching cases where old nodes can be
* reused because their next fields won't change. Statistically, at the
* default threshhold, only about one-sixth of them need cloning. (The
* nodes they replace will be garbage collectable as soon as they are no
* longer referenced by any reader thread that may be in the midst of
* traversing table right now.)
*/
for (int i = 0; i < oldCapacity; i++)
{
// We need to guarantee that any existing reads of old Map can
// proceed. So we cannot yet null out each bin.
Entry e = oldTable[i];
if (e != null)
{
int idx = e.hash & mask;
Entry next = e.next;
// Single node on list
if (next == null)
{
newTable[idx] = e;
}
else
{
// Reuse trailing consecutive sequence of all same bit
Entry lastRun = e;
int lastIdx = idx;
for (Entry last = next; last != null; last = last.next)
{
int k = last.hash & mask;
if (k != lastIdx)
{
lastIdx = k;
lastRun = last;
}
}
newTable[lastIdx] = lastRun;
// Clone all remaining nodes
for (Entry p = e; p != lastRun; p = p.next)
{
int k = p.hash & mask;
newTable[k] = new Entry(p.hash, p.key, p.value, newTable[k]);
}
}
}
}
table = newTable;
}
/**
* Removes the key (and its corresponding value) from this table. This
* method does nothing if the key is not in the table.
*
* @param key
* the key that needs to be removed.
* @return the value to which the key had been mapped in this table, or
* <code>null</code> if the key did not have a mapping.
* @exception NullPointerException
* if the key is <code>null</code>.
*/
public Object remove(Object key)
{
return remove(key, null);
}
/**
* Removes the (key, value) pair from this table. This method does nothing
* if the key is not in the table, or if the key is associated with a
* different value. This method is needed by EntrySet.
*
* @param key
* the key that needs to be removed.
* @param value
* the associated value. If the value is null, it means "any
* value".
* @return the value to which the key had been mapped in this table, or
* <code>null</code> if the key did not have a mapping.
* @exception NullPointerException
* if the key is <code>null</code>.
*/
protected Object remove(Object key, Object value)
{
/*
* Find the entry, then 1. Set value field to null, to force get() to
* retry 2. Rebuild the list without this entry. All entries following
* removed node can stay in list, but all preceeding ones need to be
* cloned. Traversals rely on this strategy to ensure that elements will
* not be repeated during iteration.
*/
int hash = hash(key);
Segment seg = segments[hash & SEGMENT_MASK];
synchronized (seg)
{
Entry[] tab = table;
int index = hash & (tab.length - 1);
Entry first = tab[index];
Entry e = first;
for (;;)
{
if (e == null)
{
return null;
}
if (e.hash == hash && eq(key, e.key))
{
break;
}
e = e.next;
}
Object oldValue = e.value;
if (value != null && !value.equals(oldValue))
{
return null;
}
e.value = null;
Entry head = e.next;
for (Entry p = first; p != e; p = p.next)
{
head = new Entry(p.hash, p.key, p.value, head);
}
tab[index] = head;
seg.count--;
return oldValue;
}
}
/**
* Returns <tt>true</tt> if this map maps one or more keys to the
* specified value. Note: This method requires a full internal traversal of
* the hash table, and so is much slower than method <tt>containsKey</tt>.
*
* @param value
* value whose presence in this map is to be tested.
* @return <tt>true</tt> if this map maps one or more keys to the
* specified value.
* @exception NullPointerException
* if the value is <code>null</code>.
*/
public boolean containsValue(Object value)
{
if (value == null)
{
throw new IllegalArgumentException("Value must not be null");
}
for (int s = 0; s < segments.length; ++s)
{
Segment seg = segments[s];
Entry[] tab;
synchronized (seg)
{
tab = table;
}
for (int i = s; i < tab.length; i += segments.length)
{
for (Entry e = tab[i]; e != null; e = e.next)
{
if (value.equals(e.value))
{
return true;
}
}
}
}
return false;
}
/**
* Tests if some key maps into the specified value in this table. This
* operation is more expensive than the <code>containsKey</code> method.
* <p>
*
* Note that this method is identical in functionality to containsValue,
* (which is part of the Map interface in the collections framework).
*
* @param value
* a value to search for.
* @return <code>true</code> if and only if some key maps to the
* <code>value</code> argument in this table as determined by the
* <tt>equals</tt> method; <code>false</code> otherwise.
* @exception NullPointerException
* if the value is <code>null</code>.
* @see #containsKey(Object)
* @see #containsValue(Object)
* @see Map
*/
public boolean contains(Object value)
{
return containsValue(value);
}
/**
* Copies all of the mappings from the specified map to this one.
*
* These mappings replace any mappings that this map had for any of the keys
* currently in the specified Map.
*
* @param t
* Mappings to be stored in this map.
*/
public void putAll(Map t)
{
int n = t.size();
if (n == 0)
{
return;
}
// Expand enough to hold at least n elements without resizing.
// We can only resize table by factor of two at a time.
// It is faster to rehash with fewer elements, so do it now.
for (;;)
{
Entry[] tab;
int max;
synchronized (segments[0])
{ // must synch on some segment. pick 0.
tab = table;
max = threshold * CONCURRENCY_LEVEL;
}
if (n < max)
{
break;
}
resize(0, tab);
}
for (Iterator it = t.entrySet().iterator(); it.hasNext();)
{
Map.Entry entry = (Map.Entry)it.next();
put(entry.getKey(), entry.getValue());
}
}
/**
* Removes all mappings from this map.
*/
public void clear()
{
// We don't need all locks at once so long as locks
// are obtained in low to high order
for (int s = 0; s < segments.length; ++s)
{
Segment seg = segments[s];
synchronized (seg)
{
Entry[] tab = table;
for (int i = s; i < tab.length; i += segments.length)
{
for (Entry e = tab[i]; e != null; e = e.next)
{
e.value = null;
}
tab[i] = null;
seg.count = 0;
}
}
}
}
/**
* Returns a shallow copy of this <tt>ConcurrentHashMap</tt> instance: the
* keys and values themselves are not cloned.
*
* @return a shallow copy of this map.
*/
public Object clone()
{
// We cannot call super.clone, since it would share final segments
// array,
// and there's no way to reassign finals.
return new ConcurrentHashMap(this);
}
// Views
protected transient Set keySet = null;
protected transient Set entrySet = null;
protected transient Collection values = null;
/**
* Returns a set view of the keys contained in this map. The set is backed
* by the map, so changes to the map are reflected in the set, and
* vice-versa. The set supports element removal, which removes the
* corresponding mapping from this map, via the <tt>Iterator.remove</tt>,
* <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and
* <tt>clear</tt> operations. It does not support the <tt>add</tt> or
* <tt>addAll</tt> operations.
*
* @return a set view of the keys contained in this map.
*/
public Set keySet()
{
Set ks = keySet;
return (ks != null) ? ks : (keySet = new KeySet());
}
private class KeySet extends AbstractSet
{
/**
* @see java.util.Set#iterator()
*/
public Iterator iterator()
{
return new KeyIterator();
}
/**
* @see java.util.Set#size()
*/
public int size()
{
return ConcurrentHashMap.this.size();
}
/**
* @see java.util.Set#contains(java.lang.Object)
*/
public boolean contains(Object o)
{
return ConcurrentHashMap.this.containsKey(o);
}
/**
* @see java.util.Set#remove(java.lang.Object)
*/
public boolean remove(Object o)
{
return ConcurrentHashMap.this.remove(o) != null;
}
/**
* @see java.util.Set#clear()
*/
public void clear()
{
ConcurrentHashMap.this.clear();
}
}
/**
* Returns a collection view of the values contained in this map. The
* collection is backed by the map, so changes to the map are reflected in
* the collection, and vice-versa. The collection supports element removal,
* which removes the corresponding mapping from this map, via the
* <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
* <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
* operations. It does not support the <tt>add</tt> or <tt>addAll</tt>
* operations.
*
* @return a collection view of the values contained in this map.
*/
public Collection values()
{
Collection vs = values;
return (vs != null) ? vs : (values = new Values());
}
private class Values extends AbstractCollection
{
/**
* @see java.util.AbstractCollection#iterator()
*/
public Iterator iterator()
{
return new ValueIterator();
}
/**
* @see java.util.AbstractCollection#size()
*/
public int size()
{
return ConcurrentHashMap.this.size();
}
/**
* @see java.util.AbstractCollection#contains(java.lang.Object)
*/
public boolean contains(Object o)
{
return ConcurrentHashMap.this.containsValue(o);
}
/**
* @see java.util.AbstractCollection#clear()
*/
public void clear()
{
ConcurrentHashMap.this.clear();
}
}
/**
* Returns a collection view of the mappings contained in this map. Each
* element in the returned collection is a <tt>Map.Entry</tt>. The
* collection is backed by the map, so changes to the map are reflected in
* the collection, and vice-versa. The collection supports element removal,
* which removes the corresponding mapping from the map, via the
* <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
* <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
* operations. It does not support the <tt>add</tt> or <tt>addAll</tt>
* operations.
*
* @return a collection view of the mappings contained in this map.
*/
public Set entrySet()
{
Set es = entrySet;
return (es != null) ? es : (entrySet = new EntrySet());
}
private class EntrySet extends AbstractSet
{
/**
* @see java.util.Set#iterator()
*/
public Iterator iterator()
{
return new HashIterator();
}
/**
* @see java.util.Set#contains(java.lang.Object)
*/
public boolean contains(Object o)
{
if (!(o instanceof Map.Entry))
{
return false;
}
Map.Entry entry = (Map.Entry)o;
Object v = ConcurrentHashMap.this.get(entry.getKey());
return v != null && v.equals(entry.getValue());
}
/**
* @see java.util.Set#remove(java.lang.Object)
*/
public boolean remove(Object o)
{
if (!(o instanceof Map.Entry))
{
return false;
}
Map.Entry e = (Map.Entry)o;
return ConcurrentHashMap.this.remove(e.getKey(), e.getValue()) != null;
}
/**
* @see java.util.Set#size()
*/
public int size()
{
return ConcurrentHashMap.this.size();
}
/**
* @see java.util.Set#clear()
*/
public void clear()
{
ConcurrentHashMap.this.clear();
}
}
/**
* Returns an enumeration of the keys in this table.
*
* @return an enumeration of the keys in this table.
* @see Enumeration
* @see #elements()
* @see #keySet()
* @see Map
*/
public Enumeration keys()
{
return new KeyIterator();
}
/**
* Returns an enumeration of the values in this table. Use the Enumeration
* methods on the returned object to fetch the elements sequentially.
*
* @return an enumeration of the values in this table.
* @see java.util.Enumeration
* @see #keys()
* @see #values()
* @see Map
*/
public Enumeration elements()
{
return new ValueIterator();
}
/**
* ConcurrentHashMap collision list entry.
*/
protected static class Entry implements Map.Entry
{
/*
* The use of volatile for value field ensures that we can detect status
* changes without synchronization. The other fields are never changed,
* and are marked as final.
*/
protected final Object key;
protected volatile Object value;
protected final int hash;
protected final Entry next;
Entry(int hash, Object key, Object value, Entry next)
{
this.value = value;
this.hash = hash;
this.key = key;
this.next = next;
}
// Map.Entry Ops
/**
* @see java.util.Map.Entry#getKey()
*/
public Object getKey()
{
return key;
}
/**
* Get the value. Note: In an entrySet or entrySet.iterator, unless you
* can guarantee lack of concurrent modification,
* <tt>getValue</tt> <em>might</em> return null, reflecting the fact
* that the entry has been concurrently removed. However, there are no
* assurances that concurrent removals will be reflected using this
* method.
*
* @return the current value, or null if the entry has been detectably
* removed.
*/
public Object getValue()
{
return value;
}
/**
* Set the value of this entry. Note: In an entrySet or
* entrySet.iterator), unless you can guarantee lack of concurrent
* modification, <tt>setValue</tt> is not strictly guaranteed to
* actually replace the value field obtained via the <tt>get</tt>
* operation of the underlying hash table in multithreaded applications.
* If iterator-wide synchronization is not used, and any other
* concurrent <tt>put</tt> or <tt>remove</tt> operations occur,
* sometimes even to <em>other</em> entries, then this change is not
* guaranteed to be reflected in the hash table. (It might, or it might
* not. There are no assurances either way.)
*
* @param value
* the new value.
* @return the previous value, or null if entry has been detectably
* removed.
* @exception NullPointerException
* if the value is <code>null</code>.
*
*/
public Object setValue(Object value)
{
if (value == null)
{
throw new IllegalArgumentException("Value must not be null");
}
Object oldValue = this.value;
this.value = value;
return oldValue;
}
/**
* @see java.util.Map.Entry#equals(java.lang.Object)
*/
public boolean equals(Object o)
{
if (!(o instanceof Map.Entry))
{
return false;
}
Map.Entry e = (Map.Entry)o;
return (key.equals(e.getKey()) && value.equals(e.getValue()));
}
/**
* @see java.util.Map.Entry#hashCode()
*/
public int hashCode()
{
return key.hashCode() ^ value.hashCode();
}
/**
* @see java.lang.Object#toString()
*/
public String toString()
{
return key + "=" + value;
}
}
protected class HashIterator implements Iterator, Enumeration
{
protected final Entry[] tab; // snapshot of table
protected int index; // current slot
protected Entry entry = null; // current node of slot
protected Object currentKey; // key for current node
protected Object currentValue; // value for current node
protected Entry lastReturned = null; // last node returned by next
protected HashIterator()
{
// force all segments to synch
synchronized (segments[0])
{
tab = table;
}
for (int i = 1; i < segments.length; ++i)
{
segments[i].synch();
}
index = tab.length - 1;
}
/**
* @see java.util.Enumeration#hasMoreElements()
*/
public boolean hasMoreElements()
{
return hasNext();
}
/**
* @see java.util.Enumeration#nextElement()
*/
public Object nextElement()
{
return next();
}
/**
* @see java.util.Iterator#hasNext()
*/
public boolean hasNext()
{
/*
* currentkey and currentValue are set here to ensure that next()
* returns normally if hasNext() returns true. This avoids surprises
* especially when final element is removed during traversal --
* instead, we just ignore the removal during current traversal.
*/
for (;;)
{
if (entry != null)
{
Object v = entry.value;
if (v != null)
{
currentKey = entry.key;
currentValue = v;
return true;
}
else
{
entry = entry.next;
}
}
while (entry == null && index >= 0)
{
entry = tab[index--];
}
if (entry == null)
{
currentKey = currentValue = null;
return false;
}
}
}
protected Object returnValueOfNext()
{
return entry;
}
/**
* @see java.util.Iterator#next()
*/
public Object next()
{
if (currentKey == null && !hasNext())
{
throw new NoSuchElementException();
}
Object result = returnValueOfNext();
lastReturned = entry;
currentKey = currentValue = null;
entry = entry.next;
return result;
}
/**
* @see java.util.Iterator#remove()
*/
public void remove()
{
if (lastReturned == null)
{
throw new IllegalStateException();
}
ConcurrentHashMap.this.remove(lastReturned.key);
lastReturned = null;
}
}
protected class KeyIterator extends HashIterator
{
protected Object returnValueOfNext()
{
return currentKey;
}
}
protected class ValueIterator extends HashIterator
{
protected Object returnValueOfNext()
{
return currentValue;
}
}
/**
* Save the state of the <tt>ConcurrentHashMap</tt> instance to a stream
* (i.e., serialize it).
* @param s
* @throws IOException
*
* @serialData An estimate of the table size, followed by the key (Object)
* and value (Object) for each key-value mapping, followed by a
* null pair. The key-value mappings are emitted in no
* particular order.
*/
private void writeObject(java.io.ObjectOutputStream s) throws IOException
{
// Write out the loadfactor, and any hidden stuff
s.defaultWriteObject();
// Write out capacity estimate. It is OK if this
// changes during the write, since it is only used by
// readObject to set initial capacity, to avoid needless resizings.
int cap;
synchronized (segments[0])
{
cap = table.length;
}
s.writeInt(cap);
// Write out keys and values (alternating)
for (int k = 0; k < segments.length; ++k)
{
Segment seg = segments[k];
Entry[] tab;
synchronized (seg)
{
tab = table;
}
for (int i = k; i < tab.length; i += segments.length)
{
for (Entry e = tab[i]; e != null; e = e.next)
{
s.writeObject(e.key);
s.writeObject(e.value);
}
}
}
s.writeObject(null);
s.writeObject(null);
}
/**
* Reconstitute the <tt>ConcurrentHashMap</tt> instance from a stream
* (i.e., deserialize it).
* @param s
* @throws IOException
* @throws ClassNotFoundException
*/
private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException
{
// Read in the threshold, loadfactor, and any hidden stuff
s.defaultReadObject();
int cap = s.readInt();
table = newTable(cap);
for (int i = 0; i < segments.length; ++i)
{
segments[i] = new Segment();
}
// Read the keys and values, and put the mappings in the table
for (;;)
{
Object key = s.readObject();
Object value = s.readObject();
if (key == null)
{
break;
}
put(key, value);
}
}
}
*/
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import java.util.Arrays;
import java.util.Comparator;
import java.util.concurrent.Semaphore;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* This program animates a sort algorithm.
* @version 1.01 2007-05-18
* @author Cay Horstmann
*/
public class AlgorithmAnimation
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new AnimationFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* This frame shows the array as it is sorted, together with buttons to single-step the animation or
* to run it without interruption.
*/
class AnimationFrame extends JFrame
{
public AnimationFrame()
{
ArrayComponent comp = new ArrayComponent();
add(comp, BorderLayout.CENTER);
final Sorter sorter = new Sorter(comp);
JButton runButton = new JButton("Run");
runButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
sorter.setRun();
}
});
JButton stepButton = new JButton("Step");
stepButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
sorter.setStep();
}
});
JPanel buttons = new JPanel();
buttons.add(runButton);
buttons.add(stepButton);
add(buttons, BorderLayout.NORTH);
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
Thread t = new Thread(sorter);
t.start();
}
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 300;
}
/**
* This runnable executes a sort algorithm. When two elements are compared, the algorithm pauses and
* updates a component.
*/
class Sorter implements Runnable
{
/**
* Constructs a Sorter.
* @param values the array to be sorted
* @param comp the component on which to display the sorting progress
*/
public Sorter(ArrayComponent comp)
{
values = new Double[VALUES_LENGTH];
for (int i = 0; i < values.length; i++)
values[i] = new Double(Math.random());
this.component = comp;
this.gate = new Semaphore(1);
this.run = false;
}
/**
* Sets the sorter to "run" mode. Called on the event dispatch thread.
*/
public void setRun()
{
run = true;
gate.release();
}
/**
* Sets the sorter to "step" mode. Called on the event dispatch thread.
*/
public void setStep()
{
run = false;
gate.release();
}
public void run()
{
Comparator<Double> comp = new Comparator<Double>()
{
public int compare(Double i1, Double i2)
{
component.setValues(values, i1, i2);
try
{
if (run) Thread.sleep(DELAY);
else gate.acquire();
}
catch (InterruptedException exception)
{
Thread.currentThread().interrupt();
}
return i1.compareTo(i2);
}
};
Arrays.sort(values, comp);
component.setValues(values, null, null);
}
private Double[] values;
private ArrayComponent component;
private Semaphore gate;
private static final int DELAY = 100;
private volatile boolean run;
private static final int VALUES_LENGTH = 30;
}
/**
* This component draws an array and marks two elements in the array.
*/
class ArrayComponent extends JComponent
{
/**
* Sets the values to be painted. Called on the sorter thread.
* @param values the array of values to display
* @param marked1 the first marked element
* @param marked2 the second marked element
*/
public synchronized void setValues(Double[] values, Double marked1, Double marked2)
{
this.values = values.clone();
this.marked1 = marked1;
this.marked2 = marked2;
repaint();
}
public synchronized void paintComponent(Graphics g) // Called on the event dispatch thread
{
if (values == null) return;
Graphics2D g2 = (Graphics2D) g;
int width = getWidth() / values.length;
for (int i = 0; i < values.length; i++)
{
double height = values[i] * getHeight();
Rectangle2D bar = new Rectangle2D.Double(width * i, 0, width, height);
if (values[i] == marked1 || values[i] == marked2) g2.fill(bar);
else g2.draw(bar);
}
}
private Double marked1;
private Double marked2;
private Double[] values;
}
import java.io.Serializable;
import java.util.Comparator;
*/
public class InvertibleComparator implements Comparator, Serializable {
private final Comparator comparator;
private boolean ascending = true;
/**
* Create an InvertibleComparator that sorts ascending by default.
* For the actual comparison, the specified Comparator will be used.
* @param comparator the comparator to decorate
*/
public InvertibleComparator(Comparator comparator) {
this.comparator = comparator;
}
/**
* Create an InvertibleComparator that sorts based on the provided order.
* For the actual comparison, the specified Comparator will be used.
* @param comparator the comparator to decorate
* @param ascending the sort order: ascending (true) or descending (false)
*/
public InvertibleComparator(Comparator comparator, boolean ascending) {
this.comparator = comparator;
setAscending(ascending);
}
/**
* Specify the sort order: ascending (true) or descending (false).
*/
public void setAscending(boolean ascending) {
this.ascending = ascending;
}
/**
* Return the sort order: ascending (true) or descending (false).
*/
public boolean isAscending() {
return ascending;
}
/**
* Invert the sort order: ascending -> descending or
* descending -> ascending.
*/
public void invertOrder() {
this.ascending = !this.ascending;
}
public int compare(Object o1, Object o2) {
int result = this.comparator.compare(o1, o2);
if (result != 0) {
// Invert the order if it is a reverse sort.
if (!this.ascending) {
if (Integer.MIN_VALUE == result) {
result = Integer.MAX_VALUE;
}
else {
result *= -1;
}
}
return result;
}
return 0;
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof InvertibleComparator)) {
return false;
}
InvertibleComparator other = (InvertibleComparator) obj;
return (this.comparator.equals(other.comparator) && this.ascending == other.ascending);
}
public int hashCode() {
return this.comparator.hashCode();
}
public String toString() {
return "InvertibleComparator: [" + this.comparator + "]; ascending=" + this.ascending;
}
}