java - 多线程并发情况下Map.containsKey() 判断有问题
黄舟
黄舟 2017-04-18 10:49:53
0
3
1228

有下面一段代码:

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() 在多线程的情况下会判断不准确。

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

黄舟
黄舟

人生最曼妙的风景,竟是内心的淡定与从容!

全部回覆(3)
PHPzhong

ConcurrentHashMap的doc上有一段

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

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

update operations holding

upon their onset.

裡面的get方法不加鎖,get方法只是拿到最新完成update的值。 locks.containsKey(lockName)没有锁来保证线程安全的。而且感觉ConcurrentHashMap的使用场景并不是用containsKey来保证更新操作只进行一次,而是用putIfAbsent

所以題主方法中的locks.containsKey(lockName)沒有鎖定來保證執行緒安全的。而且感覺🎜的使用場景並不是用containsKey來確保更新操作只進行一次,而是用putIfAbsent來保證。 🎜
小葫芦

ConcurrentMap保證的是單次操作的原子性,而不是多次操作。

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

迷茫

使用putIfAbsent方法。

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板