2010-12-04 89 views
4

如果在并行哈希映射上进行当前操作,如何执行安全获取? (喜欢的putIfAbsent同样的事情)Java ConcurrentHashMap原子获得如果存在

不好的例子,不是很线程安全(检查然后采取行动的情况):

ConcurrentMap<String, SomeObject> concMap = new ... 

//... many putIfAbsent and remove operations 

public boolean setOption(String id, Object option){ 
    SomeObject obj = concMap.get(id); 

    if (obj != null){ 
     //what if this key has been removed from the map? 
     obj.setOption(option); 
     return true; 
    } 

    // in the meantime a putIfAbsent may have been called on the map and then this 
    //setOption call is no longer correct 

    return false; 
} 

另一个不好的例子是:

public boolean setOption(String id, Object option){ 
     if (concMap.contains(id)){ 
      concMap.get(id).setOption(option); 
      return true; 
     } 
     return false; 
    } 

这里的希望的事情就是不是通过同步它们来添加,删除和获取操作的瓶颈。

感谢

+0

我不明白你对“这个setOption调用”的评论 - 你指哪个setOption方法?你的代码中有两个。 – 2010-12-04 14:02:06

+0

这只是存储在地图中的一些通用对象的泛型集方法。 – cdmihai 2010-12-04 14:06:15

+0

如果键值对可能从地图中删除,则您的第二个示例可能有缺陷。这可能发生在包含测试和get之间。 – 2010-12-04 14:26:30

回答

1

你似乎试图做的是锁定一个键在多个操作。只有每个操作是原子的。这并不是简单的方法来锁定一个键,只是锁定地图。

但是,在“如果我删除一个密钥时怎么办”的情况下,你所能做的就是延迟删除操作,直到setOption被调用。结果应该是一样的。

您似乎试图解决可能不需要解决的问题。你还没有解释为什么在一个密钥被删除后或者等待被删除的时候调用setOption是不好的。

2

不要使用containsKey/get,只需调用get。如果该方法返回null那么密钥不存在,否则密钥存在,并且您获得了在get时映射到的值。

从文档:

返回的值以指定键被映射,或空如果此映射包含该键的映射。

这是你的第二个例子应该是什么样子:

public boolean setOption(String id, Object option) { 

    SomeObject opt = concMap.get(id); 
    if (opt == null) 
     return false; 

    opt.setOption(option); 
    return true; 
} 
+0

但是如果get返回null,因为在调用时该键不存在,但是在get调用和它返回的测试之间,该键被添加到映射中(从另一个线程,代码中的其他地方)? – cdmihai 2010-12-04 14:09:41

6

get()方法上ConcurrentHashMap是原子。由于该映射不允许空值,所以get()实现“如果存在”:如果结果为null,则该键不存在。

0

如果您需要做的ConcurrentMap单个按键多次操作,您可以使用锁分离技术以减少争,这里与番石榴框架的例子:

private Striped<Lock> lock; 
    public boolean setOption(String id, Object option) { 
     try { 
     Lock lock = concMap.get(id); 
     lock.lock(); 
      if (concMap.contains(id)){ 
      concMap.get(id).setOption(option); 
     return true; 
    } 
    return false; 
     } finally { 
     lock.unlock(); 
     } 
    } 

或者说,自从Java 8 :ConcurrentMap.compute是一种新原子方法,请参阅它是如何在一个键上完成的:

concMap.compute(keyId, (key, value) -> { 
    dosmth; ... return key; }); 

ps可能的变化是与ConcurrentMap.computeIfPresent()等。