A version of Hashtable supporting concurrency for both retrievals and updates : Concurrent « Collections Data Structure « Java





A version of Hashtable supporting concurrency for both retrievals and updates

   
/*
 File: ConcurrentHashMap

 Written by Doug Lea. Adapted and released, under explicit
 permission, from JDK1.2 HashMap.java and Hashtable.java which
 carries the following copyright:

 * Copyright 1997 by Sun Microsystems, Inc.,
 * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information
 * of Sun Microsystems, Inc. ("Confidential Information").  You
 * shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement
 * you entered into with Sun.

 History:
 Date       Who                What
 26nov2000  dl               Created, based on ConcurrentReaderHashMap
 12jan2001  dl               public release
 17nov2001  dl               Minor tunings
 24oct2003  dl               Segment implements Serializable
 */


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;


/**
 * A version of Hashtable supporting concurrency for both retrievals and
 * updates:
 * 
 * <dl>
 * <dt> Retrievals
 * 
 * <dd> Retrievals may overlap updates. (This is the same policy as
 * ConcurrentReaderHashMap.) Successful retrievals using get(key) and
 * containsKey(key) usually run without locking. Unsuccessful retrievals (i.e.,
 * when the key is not present) do involve brief synchronization (locking).
 * Because retrieval operations can ordinarily overlap with update operations
 * (i.e., put, remove, and their derivatives), retrievals can only be guaranteed
 * to return the results of the most recently <em>completed</em> operations
 * holding upon their onset. Retrieval operations may or may not return results
 * reflecting in-progress writing operations. However, the retrieval operations
 * do always return consistent results -- either those holding before any single
 * modification or after it, but never a nonsense result. For aggregate
 * operations such as putAll and clear, concurrent reads may reflect insertion
 * or removal of only some entries.
 * <p>
 * 
 * Iterators and Enumerations (i.e., those returned by keySet().iterator(),
 * entrySet().iterator(), values().iterator(), keys(), and elements()) return
 * elements reflecting the state of the hash table at some point at or since the
 * creation of the iterator/enumeration. They will return at most one instance
 * of each element (via next()/nextElement()), but might or might not reflect
 * puts and removes that have been processed since they were created. They do
 * <em>not</em> throw ConcurrentModificationException. However, these
 * iterators are designed to be used by only one thread at a time. Passing an
 * iterator across multiple threads may lead to unpredictable results if the
 * table is being concurrently modified.
 * <p>
 * 
 * 
 * <dt> Updates
 * 
 * <dd> This class supports a hard-wired preset <em>concurrency
 * level</em> of
 * 32. This allows a maximum of 32 put and/or remove operations to proceed
 * concurrently. This level is an upper bound on concurrency, not a guarantee,
 * since it interacts with how well-strewn elements are across bins of the
 * table. (The preset value in part reflects the fact that even on large
 * multiprocessors, factors other than synchronization tend to be bottlenecks
 * when more than 32 threads concurrently attempt updates.) Additionally,
 * operations triggering internal resizing and clearing do not execute
 * concurrently with any operation.
 * <p>
 * 
 * There is <em>NOT</em> any support for locking the entire table to prevent
 * updates. This makes it imposssible, for example, to add an element only if it
 * is not already present, since another thread may be in the process of doing
 * the same thing. If you need such capabilities, consider instead using the
 * ConcurrentReaderHashMap class.
 * 
 * </dl>
 * 
 * Because of how concurrency control is split up, the size() and isEmpty()
 * methods require accumulations across 32 control segments, and so might be
 * slightly slower than you expect.
 * <p>
 * 
 * This class may be used as a direct replacement for java.util.Hashtable in any
 * application that does not rely on the ability to lock the entire table to
 * prevent updates. As of this writing, it performs much faster than Hashtable
 * in typical multi-threaded applications with multiple readers and writers.
 * Like Hashtable but unlike java.util.HashMap, this class does NOT allow
 * <tt>null</tt> to be used as a key or value.
 * <p>
 * 
 * Implementation note: A slightly faster implementation of this class will be
 * possible once planned Java Memory Model revisions are in place.
 * 
 * <p>[<a
 * href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html">
 * Introduction to this package. </a>]
 * 
 */


public class ConcurrentHashMap extends AbstractMap implements Map, Cloneable, Serializable
{
  private static final long serialVersionUID = 1L;

  /*
   * The basic strategy is an optimistic-style scheme based on the guarantee
   * that the hash table and its lists are always kept in a consistent enough
   * state to be read without locking:
   * 
   * Read operations first proceed without locking, by traversing the
   * apparently correct list of the apparently correct bin. If an entry is
   * found, but not invalidated (value field null), it is returned. If not
   * found, operations must recheck (after a memory barrier) to make sure they
   * are using both the right list and the right table (which can change under
   * resizes). If invalidated, reads must acquire main update lock to wait out
   * the update, and then re-traverse.
   * 
   * All list additions are at the front of each bin, making it easy to check
   * changes, and also fast to traverse. Entry next pointers are never
   * assigned. Remove() builds new nodes when necessary to preserve this.
   * 
   * Remove() (also clear()) invalidates removed nodes to alert read
   * operations that they must wait out the full modifications.
   * 
   * Locking for puts, removes (and, when necessary gets, etc) is controlled
   * by Segments, each covering a portion of the table. During operations
   * requiring global exclusivity (mainly resize and clear), ALL of these
   * locks are acquired at once. Note that these segments are NOT contiguous --
   * they are based on the least 5 bits of hashcodes. This ensures that the
   * same segment controls the same slots before and after resizing, which is
   * necessary for supporting concurrent retrievals. This comes at the price
   * of a mismatch of logical vs physical locality, but this seems not to be a
   * performance problem in practice.
   * 
   */

  /**
   * 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);
    }
  }
}

   
    
    
  










Related examples in the same category

1.A version of Hashtable that supports mostly-concurrent reading, but exclusive writing
2.Synchronized Queue
3.Concurrent Doubly LinkedList
4.Returns the parent of the specified URI.
5.A daemon thread that continuously dequeues Runnable instances from a queue and executes them.
6.Lazy Loading Reference
7.Utility class that provides a lazy initialization object wrapper.