ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/concurrent/ConcurrentHashMap.java
(Generate patch)

Comparing jsr166/src/main/java/util/concurrent/ConcurrentHashMap.java (file contents):
Revision 1.44 by dl, Mon Feb 9 13:28:47 2004 UTC vs.
Revision 1.45 by dl, Sat Apr 10 14:21:45 2004 UTC

# Line 49 | Line 49 | import java.io.ObjectOutputStream;
49   * and a significantly lower value can lead to thread contention. But
50   * overestimates and underestimates within an order of magnitude do
51   * not usually have much noticeable impact. A value of one is
52 < * appropriate when it is known that only one thread will modify
53 < * and all others will only read.
52 > * appropriate when it is known that only one thread will modify and
53 > * all others will only read. Also, resizing this or any other kind of
54 > * hash table is a relatively slow operation, so, when possible, it is
55 > * a good idea to provide estimates of expected table sizes in
56 > * constructors.
57   *
58 < * <p>This class implements all of the <em>optional</em> methods
59 < * of the {@link Map} and {@link Iterator} interfaces.
58 > * <p>This class and its views and iterators implement all of the
59 > * <em>optional</em> methods of the {@link Map} and {@link Iterator}
60 > * interfaces.
61   *
62   * <p> Like {@link java.util.Hashtable} but unlike {@link
63   * java.util.HashMap}, this class does NOT allow <tt>null</tt> to be
# Line 178 | Line 182 | public class ConcurrentHashMap<K, V> ext
182           * is less than two for the default load factor threshold.)
183           *
184           * Read operations can thus proceed without locking, but rely
185 <         * on a memory barrier to ensure that completed write
186 <         * operations performed by other threads are
187 <         * noticed. Conveniently, the "count" field, tracking the
188 <         * number of elements, can also serve as the volatile variable
189 <         * providing proper read/write barriers. This is convenient
190 <         * because this field needs to be read in many read operations
187 <         * anyway.
185 >         * on selected uses of volatiles to ensure that completed
186 >         * write operations performed by other threads are
187 >         * noticed. For most purposes, the "count" field, tracking the
188 >         * number of elements, serves as that volatile variable
189 >         * ensuring visibility.  This is convenient because this field
190 >         * needs to be read in many read operations anyway:
191           *
192 <         * Implementors note. The basic rules for all this are:
190 <         *
191 <         *   - All unsynchronized read operations must first read the
192 >         *   - All (unsynchronized) read operations must first read the
193           *     "count" field, and should not look at table entries if
194           *     it is 0.
195           *
196 <         *   - All synchronized write operations should write to
197 <         *     the "count" field after updating. The operations must not
198 <         *     take any action that could even momentarily cause
199 <         *     a concurrent read operation to see inconsistent
200 <         *     data. This is made easier by the nature of the read
201 <         *     operations in Map. For example, no operation
196 >         *   - All (synchronized) write operations should write to
197 >         *     the "count" field after structurally changing any bin.
198 >         *     The operations must not take any action that could even
199 >         *     momentarily cause a concurrent read operation to see
200 >         *     inconsistent data. This is made easier by the nature of
201 >         *     the read operations in Map. For example, no operation
202           *     can reveal that the table has grown but the threshold
203           *     has not yet been updated, so there are no atomicity
204           *     requirements for this with respect to reads.
205           *
206 <         * As a guide, all critical volatile reads and writes are marked
207 <         * in code comments.
206 >         * As a guide, all critical volatile reads and writes to the
207 >         * count field are marked in code comments.
208           */
209  
210          private static final long serialVersionUID = 2249069246763182397L;
# Line 214 | Line 215 | public class ConcurrentHashMap<K, V> ext
215          transient volatile int count;
216  
217          /**
218 <         * Number of updates; used for checking lack of modifications
219 <         * in bulk-read methods.
218 >         * Number of updates that alter the size of the table. This is
219 >         * used during bulk-read methods to make sure they see a
220 >         * consistent snapshot: If modCounts change during a traversal
221 >         * of segments computing size or checking contatinsValue, then
222 >         * we might have an inconsistent view of state so (usually)
223 >         * must retry.
224           */
225          transient int modCount;
226  
# Line 227 | Line 232 | public class ConcurrentHashMap<K, V> ext
232          transient int threshold;
233  
234          /**
235 <         * The per-segment table
235 >         * The per-segment table. Declared as a raw type, casted
236 >         * to HashEntry<K,V> on each use.
237           */
238 <        transient HashEntry[] table;
238 >        transient volatile HashEntry[] table;
239  
240          /**
241           * The load factor for the hash table.  Even though this value
# Line 249 | Line 255 | public class ConcurrentHashMap<K, V> ext
255           * Call only while holding lock or in constructor.
256           **/
257          void setTable(HashEntry[] newTable) {
252            table = newTable;
258              threshold = (int)(newTable.length * loadFactor);
259 <            count = count; // write-volatile
259 >            table = newTable;
260 >        }
261 >
262 >        /**
263 >         * Return properly casted first entry of bin for given hash
264 >         */
265 >        HashEntry<K,V> getFirst(int hash) {
266 >            HashEntry[] tab = table;
267 >            return (HashEntry<K,V>) tab[hash & (tab.length - 1)];
268 >        }
269 >
270 >        /**
271 >         * Read value field of an entry under lock. Called if value
272 >         * field ever appears to be null. This is possible only if a
273 >         * compiler happens to reorder a HashEntry initialization with
274 >         * its table assignment, which is legal under memory model
275 >         * but is not known to ever occur.
276 >         */
277 >        V readValueUnderLock(HashEntry<K,V> e) {
278 >            lock();
279 >            try {
280 >                return e.value;
281 >            } finally {
282 >                unlock();
283 >            }
284          }
285  
286          /* Specialized implementations of map methods */
287  
288          V get(Object key, int hash) {
289              if (count != 0) { // read-volatile
290 <                HashEntry[] tab = table;
262 <                int index = hash & (tab.length - 1);
263 <                HashEntry<K,V> e = (HashEntry<K,V>) tab[index];
290 >                HashEntry<K,V> e = getFirst(hash);
291                  while (e != null) {
292 <                    if (e.hash == hash && key.equals(e.key))
293 <                        return e.value;
292 >                    if (e.hash == hash && key.equals(e.key)) {
293 >                        V v = e.value;
294 >                        if (v != null)
295 >                            return v;
296 >                        return readValueUnderLock(e); // recheck
297 >                    }
298                      e = e.next;
299                  }
300              }
# Line 272 | Line 303 | public class ConcurrentHashMap<K, V> ext
303  
304          boolean containsKey(Object key, int hash) {
305              if (count != 0) { // read-volatile
306 <                HashEntry[] tab = table;
276 <                int index = hash & (tab.length - 1);
277 <                HashEntry<K,V> e = (HashEntry<K,V>) tab[index];
306 >                HashEntry<K,V> e = getFirst(hash);
307                  while (e != null) {
308                      if (e.hash == hash && key.equals(e.key))
309                          return true;
# Line 288 | Line 317 | public class ConcurrentHashMap<K, V> ext
317              if (count != 0) { // read-volatile
318                  HashEntry[] tab = table;
319                  int len = tab.length;
320 <                for (int i = 0 ; i < len; i++)
321 <                    for (HashEntry<K,V> e = (HashEntry<K,V>)tab[i] ; e != null ; e = e.next)
322 <                        if (value.equals(e.value))
320 >                for (int i = 0 ; i < len; i++) {
321 >                    for (HashEntry<K,V> e = (HashEntry<K,V>)tab[i];
322 >                         e != null ;
323 >                         e = e.next) {
324 >                        V v = e.value;
325 >                        if (v == null) // recheck
326 >                            v = readValueUnderLock(e);
327 >                        if (value.equals(v))
328                              return true;
329 +                    }
330 +                }
331              }
332              return false;
333          }
# Line 299 | Line 335 | public class ConcurrentHashMap<K, V> ext
335          boolean replace(K key, int hash, V oldValue, V newValue) {
336              lock();
337              try {
338 <                int c = count;
339 <                HashEntry[] tab = table;
304 <                int index = hash & (tab.length - 1);
305 <                HashEntry<K,V> first = (HashEntry<K,V>) tab[index];
306 <                HashEntry<K,V> e = first;
307 <                for (;;) {
308 <                    if (e == null)
309 <                        return false;
310 <                    if (e.hash == hash && key.equals(e.key))
311 <                        break;
338 >                HashEntry<K,V> e = getFirst(hash);
339 >                while (e != null && (e.hash != hash || !key.equals(e.key)))
340                      e = e.next;
313                }
341  
342 <                V v = e.value;
343 <                if (v == null || !oldValue.equals(v))
344 <                    return false;
345 <
346 <                e.value = newValue;
347 <                count = c; // write-volatile
321 <                return true;
322 <                
342 >                boolean replaced = false;
343 >                if (e != null && oldValue.equals(e.value)) {
344 >                    replaced = true;
345 >                    e.value = newValue;
346 >                }
347 >                return replaced;
348              } finally {
349                  unlock();
350              }
# Line 328 | Line 353 | public class ConcurrentHashMap<K, V> ext
353          V replace(K key, int hash, V newValue) {
354              lock();
355              try {
356 <                int c = count;
357 <                HashEntry[] tab = table;
333 <                int index = hash & (tab.length - 1);
334 <                HashEntry<K,V> first = (HashEntry<K,V>) tab[index];
335 <                HashEntry<K,V> e = first;
336 <                for (;;) {
337 <                    if (e == null)
338 <                        return null;
339 <                    if (e.hash == hash && key.equals(e.key))
340 <                        break;
356 >                HashEntry<K,V> e = getFirst(hash);
357 >                while (e != null && (e.hash != hash || !key.equals(e.key)))
358                      e = e.next;
342                }
359  
360 <                V v = e.value;
361 <                e.value = newValue;
362 <                count = c; // write-volatile
363 <                return v;
364 <                
360 >                V oldValue = null;
361 >                if (e != null) {
362 >                    oldValue = e.value;
363 >                    e.value = newValue;
364 >                }
365 >                return oldValue;
366              } finally {
367                  unlock();
368              }
# Line 356 | Line 373 | public class ConcurrentHashMap<K, V> ext
373              lock();
374              try {
375                  int c = count;
376 +                if (c++ > threshold) // ensure capacity
377 +                    rehash();
378                  HashEntry[] tab = table;
379                  int index = hash & (tab.length - 1);
380                  HashEntry<K,V> first = (HashEntry<K,V>) tab[index];
381 +                HashEntry<K,V> e = first;
382 +                while (e != null && (e.hash != hash || !key.equals(e.key)))
383 +                    e = e.next;
384  
385 <                for (HashEntry<K,V> e = first; e != null; e = (HashEntry<K,V>) e.next) {
386 <                    if (e.hash == hash && key.equals(e.key)) {
387 <                        V oldValue = e.value;
388 <                        if (!onlyIfAbsent)
389 <                            e.value = value;
368 <                        ++modCount;
369 <                        count = c; // write-volatile
370 <                        return oldValue;
371 <                    }
385 >                V oldValue;
386 >                if (e != null) {
387 >                    oldValue = e.value;
388 >                    if (!onlyIfAbsent)
389 >                        e.value = value;
390                  }
391 <
392 <                tab[index] = new HashEntry<K,V>(hash, key, value, first);
393 <                ++modCount;
394 <                ++c;
395 <                count = c; // write-volatile
396 <                if (c > threshold)
397 <                    setTable(rehash(tab));
380 <                return null;
391 >                else {
392 >                    oldValue = null;
393 >                    ++modCount;
394 >                    tab[index] = new HashEntry<K,V>(key, hash, first, value);
395 >                    count = c; // write-volatile
396 >                }
397 >                return oldValue;
398              } finally {
399                  unlock();
400              }
401          }
402  
403 <        HashEntry[] rehash(HashEntry[] oldTable) {
403 >        void rehash() {
404 >            HashEntry[] oldTable = table;            
405              int oldCapacity = oldTable.length;
406              if (oldCapacity >= MAXIMUM_CAPACITY)
407 <                return oldTable;
407 >                return;
408  
409              /*
410               * Reclassify nodes in each list to new Map.  Because we are
# Line 403 | Line 421 | public class ConcurrentHashMap<K, V> ext
421               */
422  
423              HashEntry[] newTable = new HashEntry[oldCapacity << 1];
424 +            threshold = (int)(newTable.length * loadFactor);
425              int sizeMask = newTable.length - 1;
426              for (int i = 0; i < oldCapacity ; i++) {
427                  // We need to guarantee that any existing reads of old Map can
# Line 435 | Line 454 | public class ConcurrentHashMap<K, V> ext
454                          // Clone all remaining nodes
455                          for (HashEntry<K,V> p = e; p != lastRun; p = p.next) {
456                              int k = p.hash & sizeMask;
457 <                            newTable[k] = new HashEntry<K,V>(p.hash,
458 <                                                             p.key,
459 <                                                             p.value,
441 <                                                             (HashEntry<K,V>) newTable[k]);
457 >                            HashEntry<K,V> n = (HashEntry<K,V>)newTable[k];
458 >                            newTable[k] = new HashEntry<K,V>(p.key, p.hash,
459 >                                                             n, p.value);
460                          }
461                      }
462                  }
463              }
464 <            return newTable;
464 >            table = newTable;
465          }
466  
467          /**
# Line 452 | Line 470 | public class ConcurrentHashMap<K, V> ext
470          V remove(Object key, int hash, Object value) {
471              lock();
472              try {
473 <                int c = count;
473 >                int c = count - 1;
474                  HashEntry[] tab = table;
475                  int index = hash & (tab.length - 1);
476                  HashEntry<K,V> first = (HashEntry<K,V>)tab[index];
459
477                  HashEntry<K,V> e = first;
478 <                for (;;) {
462 <                    if (e == null)
463 <                        return null;
464 <                    if (e.hash == hash && key.equals(e.key))
465 <                        break;
478 >                while (e != null && (e.hash != hash || !key.equals(e.key)))
479                      e = e.next;
467                }
480  
481 <                V oldValue = e.value;
482 <                if (value != null && !value.equals(oldValue))
483 <                    return null;
484 <
485 <                // All entries following removed node can stay in list, but
486 <                // all preceding ones need to be cloned.
487 <                HashEntry<K,V> newFirst = e.next;
488 <                for (HashEntry<K,V> p = first; p != e; p = p.next)
489 <                    newFirst = new HashEntry<K,V>(p.hash, p.key,
490 <                                                  p.value, newFirst);
491 <                tab[index] = newFirst;
492 <                ++modCount;
493 <                count = c-1; // write-volatile
481 >                V oldValue = null;
482 >                if (e != null) {
483 >                    V v = e.value;
484 >                    if (value == null || value.equals(v)) {
485 >                        oldValue = v;
486 >                        // All entries following removed node can stay
487 >                        // in list, but all preceding ones need to be
488 >                        // cloned.
489 >                        ++modCount;
490 >                        HashEntry<K,V> newFirst = e.next;
491 >                        for (HashEntry<K,V> p = first; p != e; p = p.next)
492 >                            newFirst = new HashEntry<K,V>(p.key, p.hash,  
493 >                                                          newFirst, p.value);
494 >                        tab[index] = newFirst;
495 >                        count = c; // write-volatile
496 >                    }
497 >                }
498                  return oldValue;
499              } finally {
500                  unlock();
# Line 486 | Line 502 | public class ConcurrentHashMap<K, V> ext
502          }
503  
504          void clear() {
505 <            lock();
506 <            try {
507 <                HashEntry[] tab = table;
508 <                for (int i = 0; i < tab.length ; i++)
509 <                    tab[i] = null;
510 <                ++modCount;
511 <                count = 0; // write-volatile
512 <            } finally {
513 <                unlock();
505 >            if (count != 0) {
506 >                lock();
507 >                try {
508 >                    HashEntry[] tab = table;
509 >                    for (int i = 0; i < tab.length ; i++)
510 >                        tab[i] = null;
511 >                    ++modCount;
512 >                    count = 0; // write-volatile
513 >                } finally {
514 >                    unlock();
515 >                }
516              }
517          }
518      }
# Line 505 | Line 523 | public class ConcurrentHashMap<K, V> ext
523       */
524      static final class HashEntry<K,V> {
525          final K key;
508        V value;
526          final int hash;
527 +        volatile V value;
528          final HashEntry<K,V> next;
529  
530 <        HashEntry(int hash, K key, V value, HashEntry<K,V> next) {
513 <            this.value = value;
514 <            this.hash = hash;
530 >        HashEntry(K key, int hash, HashEntry<K,V> next, V value) {
531              this.key = key;
532 +            this.hash = hash;
533              this.next = next;
534 +            this.value = value;
535          }
536      }
537  
# Line 604 | Line 622 | public class ConcurrentHashMap<K, V> ext
622      public boolean isEmpty() {
623          final Segment[] segments = this.segments;
624          /*
625 <         * We need to keep track of per-segment modCounts to avoid ABA
625 >         * We keep track of per-segment modCounts to avoid ABA
626           * problems in which an element in one segment was added and
627           * in another removed during traversal, in which case the
628           * table was never actually empty at any point. Note the
# Line 636 | Line 654 | public class ConcurrentHashMap<K, V> ext
654      // inherit Map javadoc
655      public int size() {
656          final Segment[] segments = this.segments;
657 +        long sum = 0;
658 +        long check = 0;
659          int[] mc = new int[segments.length];
660 <        for (;;) {
661 <            long sum = 0;
660 >        // Try at most twice to get accurate count. On failure due to
661 >        // continuous async changes in table, resort to locking.
662 >        for (int k = 0; k < 2; ++k) {
663 >            check = 0;
664 >            sum = 0;
665              int mcsum = 0;
666              for (int i = 0; i < segments.length; ++i) {
667                  sum += segments[i].count;
668                  mcsum += mc[i] = segments[i].modCount;
669              }
647            int check = 0;
670              if (mcsum != 0) {
671                  for (int i = 0; i < segments.length; ++i) {
672                      check += segments[i].count;
# Line 654 | Line 676 | public class ConcurrentHashMap<K, V> ext
676                      }
677                  }
678              }
679 <            if (check == sum) {
680 <                if (sum > Integer.MAX_VALUE)
681 <                    return Integer.MAX_VALUE;
682 <                else
683 <                    return (int)sum;
684 <            }
679 >            if (check == sum)
680 >                break;
681 >        }
682 >        if (check != sum) { // Resort to locking all segments
683 >            sum = 0;
684 >            for (int i = 0; i < segments.length; ++i)
685 >                segments[i].lock();
686 >            for (int i = 0; i < segments.length; ++i)
687 >                sum += segments[i].count;
688 >            for (int i = 0; i < segments.length; ++i)
689 >                segments[i].unlock();
690          }
691 +        if (sum > Integer.MAX_VALUE)
692 +            return Integer.MAX_VALUE;
693 +        else
694 +            return (int)sum;
695      }
696  
697  
# Line 708 | Line 739 | public class ConcurrentHashMap<K, V> ext
739      public boolean containsValue(Object value) {
740          if (value == null)
741              throw new NullPointerException();
742 +        
743 +        // See explanation of modCount use above
744  
745          final Segment[] segments = this.segments;
746          int[] mc = new int[segments.length];
747 <        for (;;) {
747 >
748 >        // Try at most twice without locking
749 >        for (int k = 0; k < 2; ++k) {
750              int sum = 0;
751              int mcsum = 0;
752              for (int i = 0; i < segments.length; ++i) {
# Line 733 | Line 768 | public class ConcurrentHashMap<K, V> ext
768              if (cleanSweep)
769                  return false;
770          }
771 +        // Resort to locking all segments
772 +        for (int i = 0; i < segments.length; ++i)
773 +            segments[i].lock();
774 +        boolean found = false;
775 +        try {
776 +            for (int i = 0; i < segments.length; ++i) {
777 +                if (segments[i].containsValue(value)) {
778 +                    found = true;
779 +                    break;
780 +                }
781 +            }
782 +        } finally {
783 +            for (int i = 0; i < segments.length; ++i)
784 +                segments[i].unlock();
785 +        }
786 +        return found;
787      }
788  
789      /**

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines