001    // Copyright 2011 Google Inc. All Rights Reserved.
002    
003    package com.google.common.util.concurrent;
004    
005    import static com.google.common.base.Preconditions.checkNotNull;
006    
007    import com.google.common.annotations.Beta;
008    import com.google.common.base.Function;
009    import com.google.common.collect.Maps;
010    
011    import java.util.Collections;
012    import java.util.Map;
013    import java.util.concurrent.ConcurrentHashMap;
014    import java.util.concurrent.atomic.AtomicLong;
015    
016    /**
017     * A map containing {@code long} values that can be atomically updated. While writes to a
018     * traditional {@code Map} rely on {@code put(K, V)}, the typical mechanism for writing to this map
019     * is {@code addAndGet(K, long)}, which adds a {@code long} to the value currently associated with
020     * {@code K}. If a key has not yet been associated with a value, its implicit value is zero.
021     *
022     * <p>Most methods in this class treat absent values and zero values identically, as individually
023     * documented. Exceptions to this are {@link #containsKey}, {@link #size}, {@link #isEmpty},
024     * {@link #asMap}, and {@link #toString}.
025     *
026     * <p>Instances of this class may be used by multiple threads concurrently. All operations are
027     * atomic unless otherwise noted.
028     *
029     * <p><b>Note:</b> If your values are always positive and less than 2^31, you may wish to use a
030     * {@link com.google.common.collect.Multiset} such as
031     * {@link com.google.common.collect.ConcurrentHashMultiset} instead.
032     *
033     * <b>Warning:</b> Unlike {@code Multiset}, entries whose values are zero are not automatically
034     * removed from the map. Instead they must be removed manually with {@link #removeAllZeros}.
035     *
036     * @author Charles Fry
037     * @since 11.0
038     */
039    @Beta
040    public final class AtomicLongMap<K> {
041      private final ConcurrentHashMap<K, AtomicLong> map;
042    
043      private AtomicLongMap(ConcurrentHashMap<K, AtomicLong> map) {
044        this.map = checkNotNull(map);
045      }
046    
047      /**
048       * Creates an {@code AtomicLongMap}.
049       */
050      public static <K> AtomicLongMap<K> create() {
051        return new AtomicLongMap<K>(new ConcurrentHashMap<K, AtomicLong>());
052      }
053    
054      /**
055       * Creates an {@code AtomicLongMap} with the same mappings as the specified {@code Map}.
056       */
057      public static <K> AtomicLongMap<K> create(Map<? extends K, ? extends Long> m) {
058        AtomicLongMap<K> result = create();
059        result.putAll(m);
060        return result;
061      }
062    
063      /**
064       * Returns the value associated with {@code key}, or zero if there is no value associated with
065       * {@code key}.
066       */
067      public long get(K key) {
068        AtomicLong atomic = map.get(key);
069        return atomic == null ? 0L : atomic.get();
070      }
071    
072      /**
073       * Increments by one the value currently associated with {@code key}, and returns the new value.
074       */
075      public long incrementAndGet(K key) {
076        return addAndGet(key, 1);
077      }
078    
079      /**
080       * Decrements by one the value currently associated with {@code key}, and returns the new value.
081       */
082      public long decrementAndGet(K key) {
083        return addAndGet(key, -1);
084      }
085    
086      /**
087       * Adds {@code delta} to the value currently associated with {@code key}, and returns the new
088       * value.
089       */
090      public long addAndGet(K key, long delta) {
091        outer: for (;;) {
092          AtomicLong atomic = map.get(key);
093          if (atomic == null) {
094            atomic = map.putIfAbsent(key, new AtomicLong(delta));
095            if (atomic == null) {
096              return delta;
097            }
098            // atomic is now non-null; fall through
099          }
100    
101          for (;;) {
102            long oldValue = atomic.get();
103            if (oldValue == 0L) {
104              // don't compareAndSet a zero
105              if (map.replace(key, atomic, new AtomicLong(delta))) {
106                return delta;
107              }
108              // atomic replaced
109              continue outer;
110            }
111    
112            long newValue = oldValue + delta;
113            if (atomic.compareAndSet(oldValue, newValue)) {
114              return newValue;
115            }
116            // value changed
117          }
118        }
119      }
120    
121      /**
122       * Increments by one the value currently associated with {@code key}, and returns the old value.
123       */
124      public long getAndIncrement(K key) {
125        return getAndAdd(key, 1);
126      }
127    
128      /**
129       * Decrements by one the value currently associated with {@code key}, and returns the old value.
130       */
131      public long getAndDecrement(K key) {
132        return getAndAdd(key, -1);
133      }
134    
135      /**
136       * Adds {@code delta} to the value currently associated with {@code key}, and returns the old
137       * value.
138       */
139      public long getAndAdd(K key, long delta) {
140        outer: for (;;) {
141          AtomicLong atomic = map.get(key);
142          if (atomic == null) {
143            atomic = map.putIfAbsent(key, new AtomicLong(delta));
144            if (atomic == null) {
145              return 0L;
146            }
147            // atomic is now non-null; fall through
148          }
149    
150          for (;;) {
151            long oldValue = atomic.get();
152            if (oldValue == 0L) {
153              // don't compareAndSet a zero
154              if (map.replace(key, atomic, new AtomicLong(delta))) {
155                return 0L;
156              }
157              // atomic replaced
158              continue outer;
159            }
160    
161            long newValue = oldValue + delta;
162            if (atomic.compareAndSet(oldValue, newValue)) {
163              return oldValue;
164            }
165            // value changed
166          }
167        }
168      }
169    
170      /**
171       * Associates {@code newValue} with {@code key} in this map, and returns the value previously
172       * associated with {@code key}, or zero if there was no such value.
173       */
174      public long put(K key, long newValue) {
175        outer: for (;;) {
176          AtomicLong atomic = map.get(key);
177          if (atomic == null) {
178            atomic = map.putIfAbsent(key, new AtomicLong(newValue));
179            if (atomic == null) {
180              return 0L;
181            }
182            // atomic is now non-null; fall through
183          }
184    
185          for (;;) {
186            long oldValue = atomic.get();
187            if (oldValue == 0L) {
188              // don't compareAndSet a zero
189              if (map.replace(key, atomic, new AtomicLong(newValue))) {
190                return 0L;
191              }
192              // atomic replaced
193              continue outer;
194            }
195    
196            if (atomic.compareAndSet(oldValue, newValue)) {
197              return oldValue;
198            }
199            // value changed
200          }
201        }
202      }
203    
204      /**
205       * Copies all of the mappings from the specified map to this map. The effect of this call is
206       * equivalent to that of calling {@code put(k, v)} on this map once for each mapping from key
207       * {@code k} to value {@code v} in the specified map. The behavior of this operation is undefined
208       * if the specified map is modified while the operation is in progress.
209       */
210      public void putAll(Map<? extends K, ? extends Long> m) {
211        for (Map.Entry<? extends K, ? extends Long> entry : m.entrySet()) {
212          put(entry.getKey(), entry.getValue());
213        }
214      }
215    
216      /**
217       * Removes and returns the value associated with {@code key}. If {@code key} is not
218       * in the map, this method has no effect and returns zero.
219       */
220      public long remove(K key) {
221        AtomicLong atomic = map.get(key);
222        if (atomic == null) {
223          return 0L;
224        }
225    
226        for (;;) {
227          long oldValue = atomic.get();
228          if (oldValue == 0L || atomic.compareAndSet(oldValue, 0L)) {
229            // only remove after setting to zero, to avoid concurrent updates
230            map.remove(key, atomic);
231            // succeed even if the remove fails, since the value was already adjusted
232            return oldValue;
233          }
234        }
235      }
236    
237      /**
238       * Removes all mappings from this map whose values are zero.
239       *
240       * <p>This method is not atomic: the map may be visible in intermediate states, where some
241       * of the zero values have been removed and others have not.
242       */
243      public void removeAllZeros() {
244        for (K key : map.keySet()) {
245          AtomicLong atomic = map.get(key);
246          if (atomic != null && atomic.get() == 0L) {
247            map.remove(key, atomic);
248          }
249        }
250      }
251    
252      /**
253       * Returns the sum of all values in this map.
254       *
255       * <p>This method is not atomic: the sum may or may not include other concurrent operations.
256       */
257      public long sum() {
258        long sum = 0L;
259        for (AtomicLong value : map.values()) {
260          sum = sum + value.get();
261        }
262        return sum;
263      }
264    
265      private transient Map<K, Long> asMap;
266    
267      /**
268       * Returns a live, read-only view of the map backing this {@code AtomicLongMap}.
269       */
270      public Map<K, Long> asMap() {
271        Map<K, Long> result = asMap;
272        return (result == null) ? asMap = createAsMap() : result;
273      }
274    
275      private Map<K, Long> createAsMap() {
276        return Collections.unmodifiableMap(
277            Maps.transformValues(map, new Function<AtomicLong, Long>() {
278              @Override
279              public Long apply(AtomicLong atomic) {
280                return atomic.get();
281              }
282            }));
283      }
284    
285      /**
286       * Returns true if this map contains a mapping for the specified key.
287       */
288      public boolean containsKey(Object key) {
289        return map.containsKey(key);
290      }
291    
292      /**
293       * Returns the number of key-value mappings in this map. If the map contains more than
294       * {@code Integer.MAX_VALUE} elements, returns {@code Integer.MAX_VALUE}.
295       */
296      public int size() {
297        return map.size();
298      }
299    
300      /**
301       * Returns {@code true} if this map contains no key-value mappings.
302       */
303      public boolean isEmpty() {
304        return map.isEmpty();
305      }
306    
307      /**
308       * Removes all of the mappings from this map. The map will be empty after this call returns.
309       *
310       * <p>This method is not atomic: the map may not be empty after returning if there were concurrent
311       * writes.
312       */
313      public void clear() {
314        map.clear();
315      }
316    
317      @Override
318      public String toString() {
319        return map.toString();
320      }
321    
322      /*
323       * ConcurrentMap operations which we may eventually add.
324       *
325       * The problem with these is that remove(K, long) has to be done in two phases by definition ---
326       * first decrementing to zero, and then removing. putIfAbsent or replace could observe the
327       * intermediate zero-state. Ways we could deal with this are:
328       *
329       * - Don't define any of the ConcurrentMap operations. This is the current state of affairs.
330       *
331       * - Define putIfAbsent and replace as treating zero and absent identically (as currently
332       *   implemented below). This is a bit surprising with putIfAbsent, which really becomes
333       *   putIfZero.
334       *
335       * - Allow putIfAbsent and replace to distinguish between zero and absent, but don't implement
336       *   remove(K, long). Without any two-phase operations it becomes feasible for all remaining
337       *   operations to distinguish between zero and absent. If we do this, then perhaps we should add
338       *   replace(key, long).
339       *
340       * - Introduce a special-value private static final AtomicLong that would have the meaning of
341       *   removal-in-progress, and rework all operations to properly distinguish between zero and
342       *   absent.
343       */
344    
345      /**
346       * If {@code key} is not already associated with a value or if {@code key} is associated with
347       * zero, associate it with {@code newValue}. Returns the previous value associated with
348       * {@code key}, or zero if there was no mapping for {@code key}.
349       */
350      long putIfAbsent(K key, long newValue) {
351        for (;;) {
352          AtomicLong atomic = map.get(key);
353          if (atomic == null) {
354            atomic = map.putIfAbsent(key, new AtomicLong(newValue));
355            if (atomic == null) {
356              return 0L;
357            }
358            // atomic is now non-null; fall through
359          }
360    
361          long oldValue = atomic.get();
362          if (oldValue == 0L) {
363            // don't compareAndSet a zero
364            if (map.replace(key, atomic, new AtomicLong(newValue))) {
365              return 0L;
366            }
367            // atomic replaced
368            continue;
369          }
370    
371          return oldValue;
372        }
373      }
374    
375      /**
376       * If {@code (key, expectedOldValue)} is currently in the map, this method replaces
377       * {@code expectedOldValue} with {@code newValue} and returns true; otherwise, this method
378       * returns false.
379       *
380       * <p>If {@code expectedOldValue} is zero, this method will succeed if {@code (key, zero)}
381       * is currently in the map, or if {@code key} is not in the map at all.
382       */
383      boolean replace(K key, long expectedOldValue, long newValue) {
384        if (expectedOldValue == 0L) {
385          return putIfAbsent(key, newValue) == 0L;
386        } else {
387          AtomicLong atomic = map.get(key);
388          return (atomic == null) ? false : atomic.compareAndSet(expectedOldValue, newValue);
389        }
390      }
391    
392      /**
393       * If {@code (key, value)} is currently in the map, this method removes it and returns
394       * true; otherwise, this method returns false.
395       */
396      boolean remove(K key, long value) {
397        AtomicLong atomic = map.get(key);
398        if (atomic == null) {
399          return false;
400        }
401    
402        long oldValue = atomic.get();
403        if (oldValue != value) {
404          return false;
405        }
406    
407        if (oldValue == 0L || atomic.compareAndSet(oldValue, 0L)) {
408          // only remove after setting to zero, to avoid concurrent updates
409          map.remove(key, atomic);
410          // succeed even if the remove fails, since the value was already adjusted
411          return true;
412        }
413    
414        // value changed
415        return false;
416      }
417    
418    }