Home  >  Q&A  >  body text

java - 多线程并发情况下Map.containsKey() 判断有问题

有下面一段代码:

package test;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class TestContain extends Thread{

       private final String key = "key";
    
       private final static ConcurrentMap<String, Object> locks = new ConcurrentHashMap<>();
        
        private static Object getLock(String lockName) {
            if (!locks.containsKey(lockName)) {
                //这一句会存在并发问题
                locks.put(lockName, new String("我是值"));
                System.out.println("加了一次");
            }
            return locks.get(lockName);
        }
        
        @Override
        public void run() {
            getLock(this.key);
        };
        
        public static void main(String[] args) {
            for (int i = 0; i < 20; i++) {
                new TestContain().start();;
            }
        }
}

输出结果:

加了一次
加了一次
加了一次

表明了Map.containsKey() 在多线程的情况下会判断不准确。

这是为什么呢? 有什么方法改进呢?

黄舟黄舟2744 days ago1149

reply all(3)I'll reply

  • PHPz

    PHPz2017-04-18 10:51:53

    There is a paragraph in

    ConcurrentHashMap’s doc

    Retrieval operations (including <tt>get</tt>) generally do not
    block, so may overlap with update operations (including

    <tt>put</tt> and <tt>remove</tt>). Retrievals reflect the results
    of the most recently completed update operations holding
    upon their onset.

    The get method inside does not lock, the get method only gets the value of the latest completed update.

    So locks.containsKey(lockName) in the main method does not have a lock to ensure thread safety. And it seems that the usage scenario of locks.containsKey(lockName)没有锁来保证线程安全的。而且感觉ConcurrentHashMap的使用场景并不是用containsKey来保证更新操作只进行一次,而是用putIfAbsent is not to use containsKey to ensure that the update operation is only performed once, but to use putIfAbsent to ensure.

    reply
    0
  • 高洛峰

    高洛峰2017-04-18 10:51:53

    ConcurrentMap guarantees the atomicity of a single operation, not multiple operations.

    Your getLock函数中包含了多次操作,ConcurrentMap没法扩大它的同步范围,你需要自己实现getLock lock.

    reply
    0
  • 迷茫

    迷茫2017-04-18 10:51:53

    Use the putIfAbsent method.

    reply
    0
  • Cancelreply