ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/jsr166e/ConcurrentHashMapV8.java
(Generate patch)

Comparing jsr166/src/jsr166e/ConcurrentHashMapV8.java (file contents):
Revision 1.15 by jsr166, Thu Sep 8 23:34:50 2011 UTC vs.
Revision 1.21 by jsr166, Sat Sep 10 05:35:24 2011 UTC

# Line 49 | Line 49 | import java.io.Serializable;
49   * are typically useful only when a map is not undergoing concurrent
50   * updates in other threads.  Otherwise the results of these methods
51   * reflect transient states that may be adequate for monitoring
52 < * purposes, but not for program control.
52 > * or estimation purposes, but not for program control.
53   *
54 < * <p> Resizing this or any other kind of hash table is a relatively
55 < * slow operation, so, when possible, it is a good idea to provide
56 < * estimates of expected table sizes in constructors. Also, for
57 < * compatibility with previous versions of this class, constructors
58 < * may optionally specify an expected {@code concurrencyLevel} as an
59 < * additional hint for internal sizing.
54 > * <p> The table is dynamically expanded when there are too many
55 > * collisions (i.e., keys that have distinct hash codes but fall into
56 > * the same slot modulo the table size), with the expected average
57 > * effect of maintaining roughly two bins per mapping. There may be
58 > * much variance around this average as mappings are added and
59 > * removed, but overall, this maintains a commonly accepted time/space
60 > * tradeoff for hash tables.  However, resizing this or any other kind
61 > * of hash table may be a relatively slow operation. When possible, it
62 > * is a good idea to provide a size estimate as an optional {@code
63 > * initialCapacity} constructor argument. An additional optional
64 > * {@code loadFactor} constructor argument provides a further means of
65 > * customizing initial table capacity by specifying the table density
66 > * to be used in calculating the amount of space to allocate for the
67 > * given number of elements.  Also, for compatibility with previous
68 > * versions of this class, constructors may optionally specify an
69 > * expected {@code concurrencyLevel} as an additional hint for
70 > * internal sizing.  Note that using many keys with exactly the same
71 > * {@code hashCode{}} is a sure way to slow down performance of any
72 > * hash table.
73   *
74   * <p>This class and its views and iterators implement all of the
75   * <em>optional</em> methods of the {@link Map} and {@link Iterator}
# Line 72 | Line 85 | import java.io.Serializable;
85   * <p><em>jsr166e note: This class is a candidate replacement for
86   * java.util.concurrent.ConcurrentHashMap.<em>
87   *
88 < * @since 1.5
88 > * @since 1.8
89   * @author Doug Lea
90   * @param <K> the type of keys maintained by this map
91   * @param <V> the type of mapped values
# Line 156 | Line 169 | public class ConcurrentHashMapV8<K, V>
169       * of alternatives: Under random hash codes, the frequency of
170       * nodes in bins follows a Poisson distribution
171       * (http://en.wikipedia.org/wiki/Poisson_distribution) with a
172 <     * parameter of 0.5 on average under the default loadFactor of
173 <     * 0.75. The expected number of locks covering different elements
174 <     * (i.e., bins with 2 or more nodes) is approximately 10% at
175 <     * steady state.  Lock contention probability for two threads
176 <     * accessing distinct elements is roughly 1 / (8 * #elements).
177 <     * Function "spread" performs hashCode randomization that improves
178 <     * the likelihood that these assumptions hold unless users define
179 <     * exactly the same value for too many hashCodes.
172 >     * parameter of about 0.5 on average, given the resizing threshold
173 >     * of 0.75, although with a large variance because of resizing
174 >     * granularity. Ignoring variance, the expected occurrences of
175 >     * list size k are (exp(-0.5) * pow(0.5, k) / factorial(k)). The
176 >     * first few values are:
177 >     *
178 >     * 0:    0.607
179 >     * 1:    0.303
180 >     * 2:    0.076
181 >     * 3:    0.012
182 >     * more: 0.002
183 >     *
184 >     * Lock contention probability for two threads accessing distinct
185 >     * elements is roughly 1 / (8 * #elements).  Function "spread"
186 >     * performs hashCode randomization that improves the likelihood
187 >     * that these assumptions hold unless users define exactly the
188 >     * same value for too many hashCodes.
189       *
190       * The table is resized when occupancy exceeds a threshold.  Only
191       * a single thread performs the resize (using field "resizing", to
# Line 197 | Line 219 | public class ConcurrentHashMapV8<K, V>
219       * and also avoids resizings when the first operation is from a
220       * putAll, constructor with map argument, or deserialization.
221       * These cases attempt to override the targetCapacity used in
222 <     * growTable (which may harmlessly fail to take effect in cases of
223 <     * races with other ongoing resizings).
222 >     * growTable. These harmlessly fail to take effect in cases of
223 >     * races with other ongoing resizings. Uses of the threshold and
224 >     * targetCapacity during attempted initializations or resizings
225 >     * are racy but fall back on checks to preserve correctness.
226       *
227       * The element count is maintained using a LongAdder, which avoids
228       * contention on updates but can encounter cache thrashing if read
229       * too frequently during concurrent access. To avoid reading so
230       * often, resizing is normally attempted only upon adding to a bin
231 <     * already holding two or more nodes. Under the default load
232 <     * factor and uniform hash distributions, the probability of this
233 <     * occurring at threshold is around 13%, meaning that only about 1
234 <     * in 8 puts check threshold (and after resizing, many fewer do
235 <     * so). But this approximation has high variance for small table
236 <     * sizes, so we check on any collision for sizes <= 64.  Further,
237 <     * to increase the probability that a resize occurs soon enough,
238 <     * we offset the threshold (see THRESHOLD_OFFSET) by the expected
239 <     * number of puts between checks. This is currently set to 8, in
216 <     * accord with the default load factor. In practice, this default
217 <     * is rarely overridden, and in any case is close enough to other
218 <     * plausible values not to waste dynamic probability computation
219 <     * for the sake of more precision.
231 >     * already holding two or more nodes. Under uniform hash
232 >     * distributions, the probability of this occurring at threshold
233 >     * is around 13%, meaning that only about 1 in 8 puts check
234 >     * threshold (and after resizing, many fewer do so). But this
235 >     * approximation has high variance for small table sizes, so we
236 >     * check on any collision for sizes <= 64.  Further, to increase
237 >     * the probability that a resize occurs soon enough, we offset the
238 >     * threshold (see THRESHOLD_OFFSET) by the expected number of puts
239 >     * between checks.
240       *
241       * Maintaining API and serialization compatibility with previous
242       * versions of this class introduces several oddities. Mainly: We
# Line 228 | Line 248 | public class ConcurrentHashMapV8<K, V>
248      /* ---------------- Constants -------------- */
249  
250      /**
251 <     * The largest allowed table capacity.  Must be a power of 2, at
252 <     * most 1<<30 to stay within Java array size limits.
251 >     * The largest possible table capacity.  This value must be
252 >     * exactly 1<<30 to stay within Java array allocation and indexing
253 >     * bounds for power of two table sizes.
254       */
255      private static final int MAXIMUM_CAPACITY = 1 << 30;
256  
# Line 240 | Line 261 | public class ConcurrentHashMapV8<K, V>
261      private static final int DEFAULT_CAPACITY = 16;
262  
263      /**
264 <     * The default load factor for this table, used when not otherwise
265 <     * specified in a constructor.
264 >     * The load factor for this table. Overrides of this value in
265 >     * constructors affect only the initial table capacity.  The
266 >     * actual floating point value isn't normally used, because it is
267 >     * simpler to rely on the expression {@code n - (n >>> 2)} for the
268 >     * associated resizing threshold.
269       */
270 <    private static final float DEFAULT_LOAD_FACTOR = 0.75f;
247 <
248 <    /**
249 <     * The default concurrency level for this table. Unused, but
250 <     * defined for compatibility with previous versions of this class.
251 <     */
252 <    private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
270 >    private static final float LOAD_FACTOR = 0.75f;
271  
272      /**
273       * The count value to offset thresholds to compensate for checking
# Line 258 | Line 276 | public class ConcurrentHashMapV8<K, V>
276       */
277      private static final int THRESHOLD_OFFSET = 8;
278  
279 +    /**
280 +     * The default concurrency level for this table. Unused except as
281 +     * a sizing hint, but defined for compatibility with previous
282 +     * versions of this class.
283 +     */
284 +    private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
285 +
286      /* ---------------- Nodes -------------- */
287  
288      /**
# Line 305 | Line 330 | public class ConcurrentHashMapV8<K, V>
330      private transient int threshold;
331      /** The target capacity; volatile to cover initialization races. */
332      private transient volatile int targetCapacity;
308    /** The target load factor for the table */
309    private transient final float loadFactor;
333  
334      // views
335      private transient KeySet<K,V> keySet;
# Line 373 | Line 396 | public class ConcurrentHashMapV8<K, V>
396              try {
397                  for (;;) {
398                      Node[] tab = table;
399 <                    int n, c;
399 >                    int n, c, m;
400                      if (tab == null)
401                          n = (c = targetCapacity) > 0 ? c : DEFAULT_CAPACITY;
402 <                    else if ((n = tab.length) < MAXIMUM_CAPACITY &&
403 <                             counter.sum() >= threshold)
404 <                        n <<= 1;
402 >                    else if ((m = tab.length) < MAXIMUM_CAPACITY &&
403 >                             counter.sum() >= (long)threshold)
404 >                        n = m << 1;
405                      else
406                          break;
407 +                    threshold = n - (n >>> 2) - THRESHOLD_OFFSET;
408                      Node[] nextTab = new Node[n];
385                    threshold = (int)(n * loadFactor) - THRESHOLD_OFFSET;
409                      if (tab != null)
410                          transfer(tab, nextTab,
411                                   new Node(SIGN_BIT, nextTab, null, null));
# Line 399 | Line 422 | public class ConcurrentHashMapV8<K, V>
422          return table;
423      }
424  
425 <    /*
425 >    /**
426       * Reclassifies nodes in each bin to new table.  Because we are
427       * using power-of-two expansion, the elements from each bin must
428       * either stay at same index, or move with a power of two
429       * offset. We eliminate unnecessary node creation by catching
430       * cases where old nodes can be reused because their next fields
431 <     * won't change.  Statistically, at the default loadFactor, only
432 <     * about one-sixth of them need cloning when a table doubles. The
433 <     * nodes they replace will be garbage collectable as soon as they
434 <     * are no longer referenced by any reader thread that may be in
435 <     * the midst of concurrently traversing table.
431 >     * won't change.  Statistically, only about one-sixth of them need
432 >     * cloning when a table doubles. The nodes they replace will be
433 >     * garbage collectable as soon as they are no longer referenced by
434 >     * any reader thread that may be in the midst of concurrently
435 >     * traversing table.
436       *
437       * Transfers are done from the bottom up to preserve iterator
438       * traversability. On each step, the old bin is locked,
# Line 547 | Line 570 | public class ConcurrentHashMapV8<K, V>
570                  }
571                  if (validated) {
572                      if (checkSize && tab.length < MAXIMUM_CAPACITY &&
573 <                        resizing == 0 && counter.sum() >= threshold)
573 >                        resizing == 0 && counter.sum() >= (long)threshold)
574                          growTable();
575                      break;
576                  }
# Line 686 | Line 709 | public class ConcurrentHashMapV8<K, V>
709                  }
710                  if (validated) {
711                      if (checkSize && tab.length < MAXIMUM_CAPACITY &&
712 <                        resizing == 0 && counter.sum() >= threshold)
712 >                        resizing == 0 && counter.sum() >= (long)threshold)
713                          growTable();
714                      break;
715                  }
# Line 752 | Line 775 | public class ConcurrentHashMapV8<K, V>
775       *
776       * Exported iterators (subclasses of ViewIterator) extract key,
777       * value, or key-value pairs as return values of Iterator.next(),
778 <     * and encapulate the it.next check as hasNext();
778 >     * and encapsulate the it.next check as hasNext();
779       *
780       * The iterator visits each valid node that was reachable upon
781       * iterator construction once. It might miss some that were added
# Line 828 | Line 851 | public class ConcurrentHashMapV8<K, V>
851      /* ---------------- Public operations -------------- */
852  
853      /**
854 <     * Creates a new, empty map with the specified initial
832 <     * capacity, load factor and concurrency level.
833 <     *
834 <     * @param initialCapacity the initial capacity. The implementation
835 <     * performs internal sizing to accommodate this many elements.
836 <     * @param loadFactor  the load factor threshold, used to control resizing.
837 <     * Resizing may be performed when the average number of elements per
838 <     * bin exceeds this threshold.
839 <     * @param concurrencyLevel the estimated number of concurrently
840 <     * updating threads. The implementation may use this value as
841 <     * a sizing hint.
842 <     * @throws IllegalArgumentException if the initial capacity is
843 <     * negative or the load factor or concurrencyLevel are
844 <     * nonpositive.
854 >     * Creates a new, empty map with the default initial table size (16),
855       */
856 <    public ConcurrentHashMapV8(int initialCapacity,
847 <                               float loadFactor, int concurrencyLevel) {
848 <        if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
849 <            throw new IllegalArgumentException();
850 <        int cap = tableSizeFor(initialCapacity);
856 >    public ConcurrentHashMapV8() {
857          this.counter = new LongAdder();
858 <        this.loadFactor = loadFactor;
853 <        this.targetCapacity = cap;
858 >        this.targetCapacity = DEFAULT_CAPACITY;
859      }
860  
861      /**
862 <     * Creates a new, empty map with the specified initial capacity
863 <     * and load factor and with the default concurrencyLevel (16).
862 >     * Creates a new, empty map with an initial table size
863 >     * accommodating the specified number of elements without the need
864 >     * to dynamically resize.
865       *
866       * @param initialCapacity The implementation performs internal
867       * sizing to accommodate this many elements.
862     * @param loadFactor  the load factor threshold, used to control resizing.
863     * Resizing may be performed when the average number of elements per
864     * bin exceeds this threshold.
868       * @throws IllegalArgumentException if the initial capacity of
869 <     * elements is negative or the load factor is nonpositive
867 <     *
868 <     * @since 1.6
869 >     * elements is negative
870       */
871 <    public ConcurrentHashMapV8(int initialCapacity, float loadFactor) {
872 <        this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL);
871 >    public ConcurrentHashMapV8(int initialCapacity) {
872 >        if (initialCapacity < 0)
873 >            throw new IllegalArgumentException();
874 >        int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
875 >                   MAXIMUM_CAPACITY :
876 >                   tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
877 >        this.counter = new LongAdder();
878 >        this.targetCapacity = cap;
879      }
880  
881      /**
882 <     * Creates a new, empty map with the specified initial capacity,
876 <     * and with default load factor (0.75) and concurrencyLevel (16).
882 >     * Creates a new map with the same mappings as the given map.
883       *
884 <     * @param initialCapacity the initial capacity. The implementation
879 <     * performs internal sizing to accommodate this many elements.
880 <     * @throws IllegalArgumentException if the initial capacity of
881 <     * elements is negative.
884 >     * @param m the map
885       */
886 <    public ConcurrentHashMapV8(int initialCapacity) {
887 <        this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
886 >    public ConcurrentHashMapV8(Map<? extends K, ? extends V> m) {
887 >        this.counter = new LongAdder();
888 >        this.targetCapacity = DEFAULT_CAPACITY;
889 >        putAll(m);
890      }
891  
892      /**
893 <     * Creates a new, empty map with a default initial capacity (16),
894 <     * load factor (0.75) and concurrencyLevel (16).
893 >     * Creates a new, empty map with an initial table size based on
894 >     * the given number of elements ({@code initialCapacity}) and
895 >     * initial table density ({@code loadFactor}).
896 >     *
897 >     * @param initialCapacity the initial capacity. The implementation
898 >     * performs internal sizing to accommodate this many elements,
899 >     * given the specified load factor.
900 >     * @param loadFactor the load factor (table density) for
901 >     * establishing the initial table size
902 >     * @throws IllegalArgumentException if the initial capacity of
903 >     * elements is negative or the load factor is nonpositive
904       */
905 <    public ConcurrentHashMapV8() {
906 <        this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
905 >    public ConcurrentHashMapV8(int initialCapacity, float loadFactor) {
906 >        this(initialCapacity, loadFactor, 1);
907      }
908  
909      /**
910 <     * Creates a new map with the same mappings as the given map.
911 <     * The map is created with a capacity of 1.5 times the number
912 <     * of mappings in the given map or 16 (whichever is greater),
913 <     * and a default load factor (0.75) and concurrencyLevel (16).
910 >     * Creates a new, empty map with an initial table size based on
911 >     * the given number of elements ({@code initialCapacity}), table
912 >     * density ({@code loadFactor}), and number of concurrently
913 >     * updating threads ({@code concurrencyLevel}).
914       *
915 <     * @param m the map
915 >     * @param initialCapacity the initial capacity. The implementation
916 >     * performs internal sizing to accommodate this many elements,
917 >     * given the specified load factor.
918 >     * @param loadFactor the load factor (table density) for
919 >     * establishing the initial table size
920 >     * @param concurrencyLevel the estimated number of concurrently
921 >     * updating threads. The implementation may use this value as
922 >     * a sizing hint.
923 >     * @throws IllegalArgumentException if the initial capacity is
924 >     * negative or the load factor or concurrencyLevel are
925 >     * nonpositive
926       */
927 <    public ConcurrentHashMapV8(Map<? extends K, ? extends V> m) {
928 <        this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
929 <        putAll(m);
927 >    public ConcurrentHashMapV8(int initialCapacity,
928 >                               float loadFactor, int concurrencyLevel) {
929 >        if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
930 >            throw new IllegalArgumentException();
931 >        if (initialCapacity < concurrencyLevel)   // Use at least as many bins
932 >            initialCapacity = concurrencyLevel;   // as estimated threads
933 >        long size = (long)(1.0 + (long)initialCapacity / loadFactor);
934 >        int cap =  ((size >= (long)MAXIMUM_CAPACITY) ?
935 >                    MAXIMUM_CAPACITY: tableSizeFor((int)size));
936 >        this.counter = new LongAdder();
937 >        this.targetCapacity = cap;
938      }
939  
940      /**
# Line 946 | Line 978 | public class ConcurrentHashMapV8<K, V>
978       * @param  key   possible key
979       * @return {@code true} if and only if the specified object
980       *         is a key in this table, as determined by the
981 <     *         {@code equals} method; {@code false} otherwise.
981 >     *         {@code equals} method; {@code false} otherwise
982       * @throws NullPointerException if the specified key is null
983       */
984      public boolean containsKey(Object key) {
# Line 1048 | Line 1080 | public class ConcurrentHashMapV8<K, V>
1080          if (table == null) {
1081              int size = m.size();
1082              int cap = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY :
1083 <                tableSizeFor(size + (size >>> 1));
1083 >                tableSizeFor(size + (size >>> 1) + 1);
1084              if (cap > targetCapacity)
1085                  targetCapacity = cap;
1086          }
# Line 1083 | Line 1115 | public class ConcurrentHashMapV8<K, V>
1115       * @param mappingFunction the function to compute a value
1116       * @return the current (existing or computed) value associated with
1117       *         the specified key, or {@code null} if the computation
1118 <     *         returned {@code null}.
1118 >     *         returned {@code null}
1119       * @throws NullPointerException if the specified key or mappingFunction
1120 <     *         is null,
1120 >     *         is null
1121       * @throws IllegalStateException if the computation detectably
1122       *         attempts a recursive update to this map that would
1123 <     *         otherwise never complete.
1123 >     *         otherwise never complete
1124       * @throws RuntimeException or Error if the mappingFunction does so,
1125 <     *         in which case the mapping is left unestablished.
1125 >     *         in which case the mapping is left unestablished
1126       */
1127      public V computeIfAbsent(K key, MappingFunction<? super K, ? extends V> mappingFunction) {
1128          if (key == null || mappingFunction == null)
# Line 1120 | Line 1152 | public class ConcurrentHashMapV8<K, V>
1152       * @param mappingFunction the function to compute a value
1153       * @return the current value associated with
1154       *         the specified key, or {@code null} if the computation
1155 <     *         returned {@code null} and the value was not otherwise present.
1155 >     *         returned {@code null} and the value was not otherwise present
1156       * @throws NullPointerException if the specified key or mappingFunction
1157 <     *         is null,
1157 >     *         is null
1158       * @throws IllegalStateException if the computation detectably
1159       *         attempts a recursive update to this map that would
1160 <     *         otherwise never complete.
1160 >     *         otherwise never complete
1161       * @throws RuntimeException or Error if the mappingFunction does so,
1162 <     *         in which case the mapping is unchanged.
1162 >     *         in which case the mapping is unchanged
1163       */
1164      public V compute(K key, MappingFunction<? super K, ? extends V> mappingFunction) {
1165          if (key == null || mappingFunction == null)
# Line 1567 | Line 1599 | public class ConcurrentHashMapV8<K, V>
1599              segments = (Segment<K,V>[])
1600                  new Segment<?,?>[DEFAULT_CONCURRENCY_LEVEL];
1601              for (int i = 0; i < segments.length; ++i)
1602 <                segments[i] = new Segment<K,V>(DEFAULT_LOAD_FACTOR);
1602 >                segments[i] = new Segment<K,V>(LOAD_FACTOR);
1603          }
1604          s.defaultWriteObject();
1605          InternalIterator it = new InternalIterator(table);
# Line 1590 | Line 1622 | public class ConcurrentHashMapV8<K, V>
1622              throws java.io.IOException, ClassNotFoundException {
1623          s.defaultReadObject();
1624          this.segments = null; // unneeded
1625 <        // initalize transient final fields
1625 >        // initialize transient final field
1626          UNSAFE.putObjectVolatile(this, counterOffset, new LongAdder());
1595        UNSAFE.putFloatVolatile(this, loadFactorOffset, DEFAULT_LOAD_FACTOR);
1627          this.targetCapacity = DEFAULT_CAPACITY;
1628  
1629          // Create all nodes, then place in table once size is known
# Line 1620 | Line 1651 | public class ConcurrentHashMapV8<K, V>
1651                              n = MAXIMUM_CAPACITY;
1652                          else {
1653                              int sz = (int)size;
1654 <                            n = tableSizeFor(sz + (sz >>> 1));
1654 >                            n = tableSizeFor(sz + (sz >>> 1) + 1);
1655                          }
1656 <                        threshold = (n - (n >>> 2)) - THRESHOLD_OFFSET;
1656 >                        threshold = n - (n >>> 2) - THRESHOLD_OFFSET;
1657                          Node[] tab = new Node[n];
1658                          int mask = n - 1;
1659                          while (p != null) {
# Line 1651 | Line 1682 | public class ConcurrentHashMapV8<K, V>
1682      // Unsafe mechanics
1683      private static final sun.misc.Unsafe UNSAFE;
1684      private static final long counterOffset;
1654    private static final long loadFactorOffset;
1685      private static final long resizingOffset;
1686      private static final long ABASE;
1687      private static final int ASHIFT;
# Line 1663 | Line 1693 | public class ConcurrentHashMapV8<K, V>
1693              Class<?> k = ConcurrentHashMapV8.class;
1694              counterOffset = UNSAFE.objectFieldOffset
1695                  (k.getDeclaredField("counter"));
1666            loadFactorOffset = UNSAFE.objectFieldOffset
1667                (k.getDeclaredField("loadFactor"));
1696              resizingOffset = UNSAFE.objectFieldOffset
1697                  (k.getDeclaredField("resizing"));
1698              Class<?> sc = Node[].class;

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines