1. Collection views are now backed by map.
2. Used bit-mixing hash function. 3. Added docs about the non-atomic nature of bulk operations. 4. Improved performance of size() operation. 5. Added atomic(Runnable) method. git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/collections/trunk@130773 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
43c829d318
commit
869725582c
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/StaticBucketMap.java,v 1.2 2002/07/10 03:35:32 mas Exp $
|
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/StaticBucketMap.java,v 1.3 2002/08/15 03:22:29 pjack Exp $
|
||||||
* $Revision: 1.2 $
|
* $Revision: 1.3 $
|
||||||
* $Date: 2002/07/10 03:35:32 $
|
* $Date: 2002/08/15 03:22:29 $
|
||||||
*
|
*
|
||||||
* ====================================================================
|
* ====================================================================
|
||||||
*
|
*
|
||||||
|
@ -60,36 +60,91 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.commons.collections;
|
package org.apache.commons.collections;
|
||||||
|
|
||||||
|
import java.util.AbstractCollection;
|
||||||
|
import java.util.AbstractSet;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A StaticBucketMap is an efficient, thread-safe implementation of
|
* A StaticBucketMap is an efficient, thread-safe implementation of
|
||||||
* <code>java.util.Map</code> that performs well in in a highly
|
* <code>java.util.Map</code> that performs well in in a highly
|
||||||
* thread-contentious environment. The map supports very efficient
|
* thread-contentious environment. The map supports very efficient
|
||||||
* <code>get</code>, <code>put</code>, <code>contains</code>, and
|
* {@link #get(Object) get}, {@link #put(Object,Object) put},
|
||||||
* <code>remove</code> operations, assuming (approximate) uniform hashing and
|
* {@link #remove(Object) remove} and {@link #containsKey(Object) containsKey}
|
||||||
* that the number of entries does not exceed the size of the map. If the
|
* operations, assuming (approximate) uniform hashing and
|
||||||
* number of entries exceeds the size of the map or if the hashcodes of the
|
* that the number of entries does not exceed the number of buckets. If the
|
||||||
|
* number of entries exceeds the number of buckets or if the hashcodes of the
|
||||||
* objects are not uniformly distributed, these operations have a worst case
|
* objects are not uniformly distributed, these operations have a worst case
|
||||||
* scenario that is proportional to the number of elements in the map
|
* scenario that is proportional to the number of elements in the map
|
||||||
* (<I>O(n)</I>).
|
* (<I>O(n)</I>).<P>
|
||||||
|
*
|
||||||
|
* Each bucket in the hash table has its own monitor, so two threads can
|
||||||
|
* safely operate on the map at the same time, often without incurring any
|
||||||
|
* monitor contention. This means that you don't have to wrap instances
|
||||||
|
* of this class with {@link java.util.Collections#synchronizedMap(Map)};
|
||||||
|
* instances are already thread-safe. Unfortunately, however, this means
|
||||||
|
* that this map implementation behaves in ways you may find disconcerting.
|
||||||
|
* Bulk operations, such as {@link #putAll(Map) putAll} or the
|
||||||
|
* {@link Collection#retainAll(Collection) retainAll} operation in collection
|
||||||
|
* views, are <I>not</I> atomic. If two threads are simultaneously
|
||||||
|
* executing
|
||||||
|
*
|
||||||
|
* <Pre>
|
||||||
|
* staticBucketMapInstance.putAll(map);
|
||||||
|
* </Pre>
|
||||||
|
*
|
||||||
|
* and
|
||||||
|
*
|
||||||
|
* <Pre>
|
||||||
|
* staticBucketMapInstance.entrySet().removeAll(map.entrySet());
|
||||||
|
* </Pre>
|
||||||
|
*
|
||||||
|
* then the results are generally random. Those two statement could cancel
|
||||||
|
* each other out, leaving <Code>staticBucketMapInstance</Code> essentially
|
||||||
|
* unchanged, or they could leave some random subset of <Code>map</Code> in
|
||||||
|
* <Code>staticBucketMapInstance</Code>.<P>
|
||||||
|
*
|
||||||
|
* Also, much like an encyclopedia, the results of {@link #size()} and
|
||||||
|
* {@link #isEmpty()} are out-of-date as soon as they are produced.<P>
|
||||||
|
*
|
||||||
|
* The iterators returned by the collection views of this class are <I>not</I>
|
||||||
|
* fail-fast. They will <I>never</I> raise a
|
||||||
|
* {@link java.util.ConcurrentModificationException}. Keys and values
|
||||||
|
* added to the map after the iterator is created do not necessarily appear
|
||||||
|
* during iteration. Similarly, the iterator does not necessarily fail to
|
||||||
|
* return keys and values that were removed after the iterator was created.<P>
|
||||||
|
*
|
||||||
|
* Finally, unlike {@link java.util.HashMap}-style implementations, this
|
||||||
|
* class <I>never</I> rehashes the map. The number of buckets is fixed
|
||||||
|
* at construction time and never altered. Performance may degrade if
|
||||||
|
* you do not allocate enough buckets upfront.<P>
|
||||||
|
*
|
||||||
|
* The {@link #atomic(Runnable)} method is provided to allow atomic iterations
|
||||||
|
* and bulk operations; however, overuse of {@link #atomic(Runnable) atomic}
|
||||||
|
* will basically result in a map that's slower than an ordinary synchronized
|
||||||
|
* {@link java.util.HashMap}.
|
||||||
|
*
|
||||||
|
* Use this class if you do not require reliable bulk operations and
|
||||||
|
* iterations, or if you can make your own guarantees about how bulk
|
||||||
|
* operations will affect the map.<P>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:bloritsch@apache.org">Berin Loritsch</a>
|
* @author <a href="mailto:bloritsch@apache.org">Berin Loritsch</a>
|
||||||
* @author <a href="mailto:g-froehlich@gmx.de">Gerhard Froehlich</a>
|
* @author <a href="mailto:g-froehlich@gmx.de">Gerhard Froehlich</a>
|
||||||
* @author <a href="mailto:mas@apache.org">Michael A. Smith</a>
|
* @author <a href="mailto:mas@apache.org">Michael A. Smith</a>
|
||||||
* @version CVS $Revision: 1.2 $ $Date: 2002/07/10 03:35:32 $
|
* @author Paul Jack
|
||||||
|
* @version CVS $Revision: 1.3 $ $Date: 2002/08/15 03:22:29 $
|
||||||
* @since Avalon 4.0
|
* @since Avalon 4.0
|
||||||
*/
|
*/
|
||||||
public final class StaticBucketMap implements Map
|
public final class StaticBucketMap implements Map
|
||||||
{
|
{
|
||||||
private static final int DEFAULT_BUCKETS = 255;
|
private static final int DEFAULT_BUCKETS = 255;
|
||||||
private final Node[] m_buckets;
|
private Node[] m_buckets;
|
||||||
private final Object[] m_locks;
|
private Lock[] m_locks;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the map with the default number of buckets (255).
|
* Initializes the map with the default number of buckets (255).
|
||||||
|
@ -106,6 +161,8 @@ public final class StaticBucketMap implements Map
|
||||||
* chances for thread contention. The fewer buckets, the more chances for
|
* chances for thread contention. The fewer buckets, the more chances for
|
||||||
* thread contention. The more buckets the fewer chances for thread
|
* thread contention. The more buckets the fewer chances for thread
|
||||||
* contention.
|
* contention.
|
||||||
|
*
|
||||||
|
* @param numBuckets the number of buckets for this map
|
||||||
*/
|
*/
|
||||||
public StaticBucketMap( int numBuckets )
|
public StaticBucketMap( int numBuckets )
|
||||||
{
|
{
|
||||||
|
@ -118,11 +175,11 @@ public final class StaticBucketMap implements Map
|
||||||
}
|
}
|
||||||
|
|
||||||
m_buckets = new Node[ size ];
|
m_buckets = new Node[ size ];
|
||||||
m_locks = new Object[ size ];
|
m_locks = new Lock[ size ];
|
||||||
|
|
||||||
for( int i = 0; i < size; i++ )
|
for( int i = 0; i < size; i++ )
|
||||||
{
|
{
|
||||||
m_locks[ i ] = new Object();
|
m_locks[ i ] = new Lock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,34 +199,23 @@ public final class StaticBucketMap implements Map
|
||||||
private final int getHash( Object key )
|
private final int getHash( Object key )
|
||||||
{
|
{
|
||||||
if( key == null ) return 0;
|
if( key == null ) return 0;
|
||||||
final int hash = key.hashCode() % m_buckets.length;
|
int hash = key.hashCode();
|
||||||
|
hash += ~(hash << 15);
|
||||||
|
hash ^= (hash >>> 10);
|
||||||
|
hash += (hash << 3);
|
||||||
|
hash ^= (hash >>> 6);
|
||||||
|
hash += ~(hash << 11);
|
||||||
|
hash ^= (hash >>> 16);
|
||||||
|
hash %= m_buckets.length;
|
||||||
return ( hash < 0 ) ? hash * -1 : hash;
|
return ( hash < 0 ) ? hash * -1 : hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain a Set for the keys. This operation crosses bucket boundaries,
|
* Returns a set view of this map's keys.
|
||||||
* so it is less efficient, and greatly increases the chance for thread
|
|
||||||
* contention.
|
|
||||||
*/
|
*/
|
||||||
public Set keySet()
|
public Set keySet()
|
||||||
{
|
{
|
||||||
Set keySet = new HashSet();
|
return new KeySet();
|
||||||
|
|
||||||
for( int i = 0; i < m_buckets.length; i++ )
|
|
||||||
{
|
|
||||||
synchronized( m_locks[ i ] )
|
|
||||||
{
|
|
||||||
Node n = m_buckets[ i ];
|
|
||||||
|
|
||||||
while( n != null )
|
|
||||||
{
|
|
||||||
keySet.add( n.key );
|
|
||||||
n = n.next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return keySet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -181,16 +227,7 @@ public final class StaticBucketMap implements Map
|
||||||
|
|
||||||
for( int i = 0; i < m_buckets.length; i++ )
|
for( int i = 0; i < m_buckets.length; i++ )
|
||||||
{
|
{
|
||||||
synchronized( m_locks[ i ] )
|
cnt += m_locks[i].size;
|
||||||
{
|
|
||||||
Node n = m_buckets[ i ];
|
|
||||||
|
|
||||||
while( n != null )
|
|
||||||
{
|
|
||||||
cnt++;
|
|
||||||
n = n.next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cnt;
|
return cnt;
|
||||||
|
@ -213,6 +250,7 @@ public final class StaticBucketMap implements Map
|
||||||
n.key = key;
|
n.key = key;
|
||||||
n.value = value;
|
n.value = value;
|
||||||
m_buckets[ hash ] = n;
|
m_buckets[ hash ] = n;
|
||||||
|
m_locks[hash].size++;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,6 +275,7 @@ public final class StaticBucketMap implements Map
|
||||||
newNode.key = key;
|
newNode.key = key;
|
||||||
newNode.value = value;
|
newNode.value = value;
|
||||||
n.next = newNode;
|
n.next = newNode;
|
||||||
|
m_locks[hash].size++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -328,23 +367,7 @@ public final class StaticBucketMap implements Map
|
||||||
*/
|
*/
|
||||||
public Collection values()
|
public Collection values()
|
||||||
{
|
{
|
||||||
ArrayList values = new ArrayList();
|
return new Values();
|
||||||
|
|
||||||
for( int i = 0; i < m_buckets.length; i++ )
|
|
||||||
{
|
|
||||||
synchronized( m_locks[ i ] )
|
|
||||||
{
|
|
||||||
Node n = m_buckets[ i ];
|
|
||||||
|
|
||||||
while( n != null )
|
|
||||||
{
|
|
||||||
values.add( n.value );
|
|
||||||
n = n.next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -354,23 +377,7 @@ public final class StaticBucketMap implements Map
|
||||||
*/
|
*/
|
||||||
public Set entrySet()
|
public Set entrySet()
|
||||||
{
|
{
|
||||||
Set entrySet = new HashSet();
|
return new EntrySet();
|
||||||
|
|
||||||
for( int i = 0; i < m_buckets.length; i++ )
|
|
||||||
{
|
|
||||||
synchronized( m_locks[ i ] )
|
|
||||||
{
|
|
||||||
Node n = m_buckets[ i ];
|
|
||||||
|
|
||||||
while( n != null )
|
|
||||||
{
|
|
||||||
entrySet.add( n );
|
|
||||||
n = n.next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return entrySet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -414,7 +421,7 @@ public final class StaticBucketMap implements Map
|
||||||
// Set the next node of the previous node to be the node after this one.
|
// Set the next node of the previous node to be the node after this one.
|
||||||
prev.next = n.next;
|
prev.next = n.next;
|
||||||
}
|
}
|
||||||
|
m_locks[hash].size--;
|
||||||
return n.value;
|
return n.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,15 +438,7 @@ public final class StaticBucketMap implements Map
|
||||||
*/
|
*/
|
||||||
public final boolean isEmpty()
|
public final boolean isEmpty()
|
||||||
{
|
{
|
||||||
for( int i = 0; i < m_buckets.length; i++ )
|
return size() == 0;
|
||||||
{
|
|
||||||
if( m_buckets[ i ] != null )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -449,10 +448,21 @@ public final class StaticBucketMap implements Map
|
||||||
{
|
{
|
||||||
for( int i = 0; i < m_buckets.length; i++ )
|
for( int i = 0; i < m_buckets.length; i++ )
|
||||||
{
|
{
|
||||||
m_buckets[ i ] = null;
|
Lock lock = m_locks[i];
|
||||||
|
synchronized (lock) {
|
||||||
|
m_buckets[ i ] = null;
|
||||||
|
lock.size = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given object is a map with the same mappings
|
||||||
|
* as this map.
|
||||||
|
*
|
||||||
|
* @return true if the given object is the a map with same mappings
|
||||||
|
* as this map
|
||||||
|
*/
|
||||||
public final boolean equals( Object obj )
|
public final boolean equals( Object obj )
|
||||||
{
|
{
|
||||||
if( obj == null ) return false;
|
if( obj == null ) return false;
|
||||||
|
@ -465,6 +475,11 @@ public final class StaticBucketMap implements Map
|
||||||
return entrySet().equals(other.entrySet());
|
return entrySet().equals(other.entrySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a hash code for this map.
|
||||||
|
*
|
||||||
|
* @return a hash code for this map
|
||||||
|
*/
|
||||||
public final int hashCode()
|
public final int hashCode()
|
||||||
{
|
{
|
||||||
int hashCode = 0;
|
int hashCode = 0;
|
||||||
|
@ -532,4 +547,214 @@ public final class StaticBucketMap implements Map
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final static class Lock {
|
||||||
|
|
||||||
|
public int size;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class EntryIterator implements Iterator {
|
||||||
|
|
||||||
|
private ArrayList current = new ArrayList();
|
||||||
|
private int bucket;
|
||||||
|
private Map.Entry last;
|
||||||
|
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
if (current.size() > 0) return true;
|
||||||
|
while (bucket < m_buckets.length) {
|
||||||
|
synchronized (m_locks[bucket]) {
|
||||||
|
Node n = m_buckets[bucket];
|
||||||
|
while (n != null) {
|
||||||
|
current.add(n);
|
||||||
|
n = n.next;
|
||||||
|
}
|
||||||
|
bucket++;
|
||||||
|
if (current.size() > 0) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Map.Entry nextEntry() {
|
||||||
|
if (!hasNext()) throw new NoSuchElementException();
|
||||||
|
last = (Map.Entry)current.remove(current.size() - 1);
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object next() {
|
||||||
|
return nextEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() {
|
||||||
|
if (last == null) throw new IllegalStateException();
|
||||||
|
StaticBucketMap.this.remove(last.getKey());
|
||||||
|
last = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ValueIterator extends EntryIterator {
|
||||||
|
|
||||||
|
public Object next() {
|
||||||
|
return nextEntry().getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class KeyIterator extends EntryIterator {
|
||||||
|
|
||||||
|
public Object next() {
|
||||||
|
return nextEntry().getKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class EntrySet extends AbstractSet {
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return StaticBucketMap.this.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
StaticBucketMap.this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator iterator() {
|
||||||
|
return new EntryIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
Map.Entry entry = (Map.Entry)o;
|
||||||
|
int hash = getHash(entry.getKey());
|
||||||
|
synchronized (m_locks[hash]) {
|
||||||
|
for (Node n = m_buckets[hash]; n != null; n = n.next) {
|
||||||
|
if (n.equals(entry)) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
Map.Entry entry = (Map.Entry)o;
|
||||||
|
int hash = getHash(entry.getKey());
|
||||||
|
synchronized (m_locks[hash]) {
|
||||||
|
for (Node n = m_buckets[hash]; n != null; n = n.next) {
|
||||||
|
if (n.equals(entry)) {
|
||||||
|
StaticBucketMap.this.remove(n.getKey());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class KeySet extends AbstractSet {
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return StaticBucketMap.this.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
StaticBucketMap.this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator iterator() {
|
||||||
|
return new KeyIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return StaticBucketMap.this.containsKey(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
int hash = getHash(o);
|
||||||
|
synchronized (m_locks[hash]) {
|
||||||
|
for (Node n = m_buckets[hash]; n != null; n = n.next) {
|
||||||
|
Object k = n.getKey();
|
||||||
|
if ((k == o) || ((k != null) && k.equals(o))) {
|
||||||
|
StaticBucketMap.this.remove(k);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class Values extends AbstractCollection {
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return StaticBucketMap.this.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
StaticBucketMap.this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator iterator() {
|
||||||
|
return new ValueIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevents any operations from occuring on this map while the
|
||||||
|
* given {@link Runnable} executes. This method can be used, for
|
||||||
|
* instance, to execute a bulk operation atomicly:
|
||||||
|
*
|
||||||
|
* <Pre>
|
||||||
|
* staticBucketMapInstance.atomic(new Runnable() {
|
||||||
|
* public void run() {
|
||||||
|
* staticBucketMapInstance.putAll(map);
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
* </Pre>
|
||||||
|
*
|
||||||
|
* It can also be used if you need a reliable iterator:
|
||||||
|
*
|
||||||
|
* <Pre>
|
||||||
|
* staticBucketMapInstance.atomic(new Runnable() {
|
||||||
|
* public void run() {
|
||||||
|
* Iterator iterator = staticBucketMapInstance.iterator();
|
||||||
|
* while (iterator.hasNext()) {
|
||||||
|
* foo(iterator.next();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
* </Pre>
|
||||||
|
*
|
||||||
|
* <B>Implementation note:</B> This method requires a lot of time
|
||||||
|
* and a ton of stack space. Essentially a recursive algorithm is used
|
||||||
|
* to enter each bucket's monitor. If you have twenty thousand buckets
|
||||||
|
* in your map, then the recursive method will be invoked twenty thousand
|
||||||
|
* times. You have been warned.
|
||||||
|
*
|
||||||
|
* @param r the code to execute atomicly
|
||||||
|
*/
|
||||||
|
public void atomic(Runnable r) {
|
||||||
|
if (r == null) throw new NullPointerException();
|
||||||
|
atomic(r, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void atomic(Runnable r, int bucket) {
|
||||||
|
if (bucket >= m_buckets.length) {
|
||||||
|
r.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
synchronized (m_locks[bucket]) {
|
||||||
|
atomic(r, bucket + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue