2016-06-04 2 views
0

я следующая картаConcurrentHashMap поставил

ConcurrentMap<K, V> myMap = new ConcurrentHashMap<>(); 

и теперь я хотел бы, чтобы вставить новый объект в этой карте атомарно, так что я могу сделать что-то вроде

V myMethod(K key, int val) { 
    return map.putIfAbsent(key, new V(val)); 
} 

но это не атомная, потому что сначала будет создан новый V, а затем вставлен в карту. Есть ли способ сделать это, не используя синхронизированный (или используя синхронизированный самый быстрый путь здесь)?

+0

http://stackoverflow.com/questions/31938613/is-my-code-thread-unsafe/31938710 –

ответ

1

Но ... ConcurrentHashMap уже использует синхронизированный внутри.

/* 
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 
* Written by Doug Lea with assistance from members of JCP JSR-166 
* Expert Group and released to the public domain, as explained at 
* http://creativecommons.org/publicdomain/zero/1.0/ 
*/ 

/** Implementation for put and putIfAbsent */ 
final V putVal(K key, V value, boolean onlyIfAbsent) { 
    if (key == null || value == null) throw new NullPointerException(); 
    int hash = spread(key.hashCode()); 
    int binCount = 0; 
    for (Node<K,V>[] tab = table;;) { 
     Node<K,V> f; int n, i, fh; 
     if (tab == null || (n = tab.length) == 0) 
      tab = initTable(); 
     else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { 
      if (casTabAt(tab, i, null, 
         new Node<K,V>(hash, key, value, null))) 
       break;     // no lock when adding to empty bin 
     } 
     else if ((fh = f.hash) == MOVED) 
      tab = helpTransfer(tab, f); 
     else { 
      V oldVal = null; 
      synchronized (f) { 
       if (tabAt(tab, i) == f) { 
        if (fh >= 0) { 
         binCount = 1; 
         for (Node<K,V> e = f;; ++binCount) { 
          K ek; 
          if (e.hash == hash && 
           ((ek = e.key) == key || 
           (ek != null && key.equals(ek)))) { 
           oldVal = e.val; 
           if (!onlyIfAbsent) 
            e.val = value; 
           break; 
          } 
          Node<K,V> pred = e; 
          if ((e = e.next) == null) { 
           pred.next = new Node<K,V>(hash, key, 
                  value, null); 
           break; 
          } 
         } 
        } 
        else if (f instanceof TreeBin) { 
         Node<K,V> p; 
         binCount = 2; 
         if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, 
                 value)) != null) { 
          oldVal = p.val; 
          if (!onlyIfAbsent) 
           p.val = value; 
         } 
        } 
       } 
      } 
      if (binCount != 0) { 
       if (binCount >= TREEIFY_THRESHOLD) 
        treeifyBin(tab, i); 
       if (oldVal != null) 
        return oldVal; 
       break; 
      } 
     } 
    } 
    addCount(1L, binCount); 
    return null; 
} 

И

* Insertion (via put or its variants) of the first node in an 
* empty bin is performed by just CASing it to the bin. This is 
* by far the most common case for put operations under most 
* key/hash distributions. Other update operations (insert, 
* delete, and replace) require locks. We do not want to waste 
* the space required to associate a distinct lock object with 
* each bin, so instead use the first node of a bin list itself as 
* a lock. Locking support for these locks relies on builtin 
* "synchronized" monitors. 

Вам не нужно указать synchronized себя.

0

Похоже, вы ищете computeIfAbsent в Java 8.

V myMethod(K key, int val) { 
    return map.computeIfAbsent(key,() -> new V(val)); 
} 

Это только создаст V объект один раз за ключ. Примечание: он все равно будет создавать объект Лямбда каждый раз (если побег анализ помещает его в стек)

+0

@jpos, но вы можете считать, что он был добавлен в Java 8, потому что не было простого способа сделать это в Java 7. примечание: Java 7 уже давно закончилась публичными обновлениями. –

0

Javadoc явно заявляет, что putIfAbsent()является атомарным.

Смежные вопросы