2017-04-02 104 views
1

使用ConcurrentMap.replace點下面是從Java併發摘錄實踐:什麼是在這個例子中

public class DelegatingVehicleTracker { 

    private final ConcurrentMap<String, Point> locations; 
    private final Map<String, Point> unmodifiableMap; 

    public DelegatingVehicleTracker(final Map<String, Point> points) { 
     this.locations = new ConcurrentHashMap<>(points); 
     this.unmodifiableMap = Collections.unmodifiableMap(this.locations); 
    } 

    public Map<String, Point> getLocations() { 
     return unmodifiableMap; 
    } 

    public Point getLocation(final String id) { 
     return locations.get(id); 
    } 

    public void setLocation(final String id, final int x, final int y) { 
     if (null == locations.replace(id, new Point(x, y))) { 
      throw new IllegalArgumentException("Invalid vehicle name: " + id); 
     } 
    } 
} 

我的問題是關於它使用ConcurrentMap.replacesetLocation方法。此方法的JavaDoc說它等價於:

if (map.containsKey(key)) { 
    return map.put(key, value); 
} else return null; 

除了該操作是以原子方式執行的。

如果我們不使用原子版本,會出現什麼問題。一種可能性是一個線程看到映射包含一個給定的鍵,並且在它爲該鍵放置一個新值之前,另一個線程刪除該鍵 - 值對,但由於示例中的類不允許移除,所以這不會發生。

另一種可能性是兩個線程試圖用不同的值替換相同的密鑰。在這種情況下,一個線程可能不會返回正確的先前值,但在此示例中我們不關心之前的值,方法setLocation返回void

因此,似乎該方法可以被重寫而沒有replace。這就是我的問題。在書中同一類的後續版本中,與上述版本幾乎相同,方法setLocation不使用replace,只是containsKey,我在想這是否會影響線程安全。

回答

1

方法setLocation不使用替換,只是中的containsKey和我 想知道,這可能會影響線程安全。

確實如此,你已經完全

描述它什麼可能出問題,如果我們不使用原子彈的版本。一個 的可能性是一個線程看到該映射包含給定鍵 ,並且在爲該鍵放置新值之前,另一個線程刪除了鍵值對的 ,但由於示例中的類不允許 刪除,這不可能發生。

這就是爲什麼ConcurrentHashMap.replace實施鎖定它試圖取代

/** 
    * Implementation for the four public remove/replace methods: 
    * Replaces node value with v, conditional upon match of cv if 
    * non-null. If resulting value is null, delete. 
    */ 
    final V replaceNode(Object key, V value, Object cv) { 
     int hash = spread(key.hashCode()); 
     for (Node<K,V>[] tab = table;;) { 
      Node<K,V> f; int n, i, fh; 
      if (tab == null || (n = tab.length) == 0 || 
       (f = tabAt(tab, i = (n - 1) & hash)) == null) 
       break; 
      else if ((fh = f.hash) == MOVED) 
       tab = helpTransfer(tab, f); 
      else { 
       V oldVal = null; 
       boolean validated = false; 
       synchronized (f) { 
        if (tabAt(tab, i) == f) { 
         if (fh >= 0) { 
          validated = true; 
          for (Node<K,V> e = f, pred = null;;) { 
           K ek; 
           if (e.hash == hash && 
            ((ek = e.key) == key || 
            (ek != null && key.equals(ek)))) { 
            V ev = e.val; 
            if (cv == null || cv == ev || 
             (ev != null && cv.equals(ev))) { 
             oldVal = ev; 
             if (value != null) 
              e.val = value; 
             else if (pred != null) 
              pred.next = e.next; 
             else 
              setTabAt(tab, i, e.next); 
            } 
            break; 
           } 
           pred = e; 
           if ((e = e.next) == null) 
            break; 
          } 
         } 
         else if (f instanceof TreeBin) { 
          validated = true; 
          TreeBin<K,V> t = (TreeBin<K,V>)f; 
          TreeNode<K,V> r, p; 
          if ((r = t.root) != null && 
           (p = r.findTreeNode(hash, key, null)) != null) { 
           V pv = p.val; 
           if (cv == null || cv == pv || 
            (pv != null && cv.equals(pv))) { 
            oldVal = pv; 
            if (value != null) 
             p.val = value; 
            else if (t.removeTreeNode(p)) 
             setTabAt(tab, i, untreeify(t.first)); 
           } 
          } 
         } 
        } 
       } 
       if (validated) { 
        if (oldVal != null) { 
         if (value == null) 
          addCount(-1L, -1); 
         return oldVal; 
        } 
        break; 
       } 
      } 
     } 
     return null; 
    } 
0

什麼可能出問題,如果我們不使用原子彈版本

沒有節點。

它的風格。您可以以任何方式實施setLocation方法,但恰巧replace是確保只有在給定位置存在於地圖中時才插入的好方法。

在書中同一類的後續版本中,與上面的版本幾乎相同,setLocation方法不使用replace,只是containsKey,我想知道這是否會影響線程安全。

它不損害線程安全。關鍵字包含在映射中的斷言不能在併發寫入中更改,因爲如果存在該關鍵字,它將不會被突然刪除,並且假定它不存在,則不會執行任何操作。

作者決定使用contains鍵,因爲第二個示例使用可變點而不是不可變點。回想一下,JCIP是爲Java 5編寫的,當時並不存在computeIfPresent等方法。因此,作者必須獲得它自己的對象才能修改它。因此,線程安全將委託給可變點,而不是車輛追蹤器本身。

相關問題