多线程Map并发读后修改

发布于 2022-09-11 20:00:16 字数 648 浏览 25 评论 0

两个线程同时操作一个全局map,如何保证值不被覆盖?

public static  Map<Integer, String> DATA_MAP = new ConcurrentHashMap<>(16);

DATA_MAP.put(3,"null,null");
  • 首先两个线程同时取Map的值
  • 使用当前时间分别替换掉value的两个null
String str = DATA_MAP.get(3);
Thread.sleep(1000);
String replaceStr = nextParam.replaceFirst("null",System.currentTimeMillis()+"");
DATA_MAP.put(nodeInfo.getNextNodeId(),replaceStr);

我现在得到的key为3的结果是:

20191233311231,null
//最后执行的会覆盖掉前一个执行的结果。 因为取值是得到的都是"null,null"

我想得到的结果是:

20191233311231,201912321321411

如何去做?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

原谅过去的我 2022-09-18 20:00:16

可以上传一下完整的代码吗? 因为这里看不到另一个线程的操作。

ConcurrentHashMap是能保证一个写操作的的原子性,你这样先取出来,改完后put进去,结果肯定是这样。(一个读操作+一个写操作,ConcurrentHashMap不能保证原子性)

眉目亦如画i 2022-09-18 20:00:16

getput 的过程放在一起加一个锁,分开的操作不能保证原子性。

许久 2022-09-18 20:00:16

可以利用ConcurrentHashMapreplace方法,demo如下

public static void main(String[] args) throws ExecutionException, InterruptedException {
        ConcurrentHashMap<Integer, String> dataMap = new ConcurrentHashMap<>(16);
        dataMap.put(3, "null,null");
        ExecutorService es = Executors.newFixedThreadPool(2);
        Future<Void> future1 = es.submit(new ReplaceNullCallable(3, dataMap));
        Future<Void> future2 = es.submit(new ReplaceNullCallable(3, dataMap));

        future1.get();
        future2.get();

        System.out.println("最终结果:" + dataMap.get(3));

    }


    public static class ReplaceNullCallable implements Callable<Void> {

        private int key;

        private ConcurrentHashMap<Integer, String> dataMap;

        public ReplaceNullCallable(int key, ConcurrentHashMap<Integer, String> dataMap) {
            this.key = key;
            this.dataMap = dataMap;
        }

        @Override
        public Void call() {
            while (true) {
                String oldValue = dataMap.get(key);
                if (!oldValue.contains("null")) {
                    break;
                }

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                String newValue = oldValue.replaceFirst("null", System.currentTimeMillis() + "");
                dataMap.replace(key, oldValue, newValue);
            }

            return null;
        }
    }
英雄似剑 2022-09-18 20:00:16

1、你用HashTable、Collections.synchronizedMap()、ConcurrentHashMap去存数据,而且你get和set在同一个synchronized里面。
2、再有,避免数据被覆盖,可以对key进行加锁,做一个加锁的方法,锁对象是操作的key,保证只有一个能操作,方法内部再用cas做一次判断,避免覆盖数据。

谈下烟灰 2022-09-18 20:00:16

ConcurrentHashMap只能保证单个操作的原子性,你先get再put,整个操作不是原子性操作,因此是会出现覆盖的问题的。用java.util.concurrent.ConcurrentHashMap#replace(K, V, V)方法的话需要加上失败重试,不然在并发的情况下基本是会出现一个线程无法完成替换的情况的。

这个需求使用java.util.concurrent.ConcurrentHashMap#computeIfPresent方法实现起来会方便一点。
jdk7可能没有这个方法,需要jdk8或以上版本


    public static void main(String[] args) throws InterruptedException {
        final ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();
        map.put(3, "null, null");
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        //模拟并发
        CountDownLatch latch = new CountDownLatch(1);
        Runnable replace = () -> {
            try {
                //模拟准备时间
                Thread.sleep(ThreadLocalRandom.current().nextLong(3000));
                //等待
                System.out.println(String.format("线程%s就绪@%d", Thread.currentThread().getId(), System.currentTimeMillis()));
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(String.format("线程%s替换前:%s", Thread.currentThread().getId(), map.get(3)));
            map.computeIfPresent(3, (k, v) -> v.replaceFirst("null", String.valueOf(System.currentTimeMillis())));
            System.out.println(String.format("线程%s替换后:%s", Thread.currentThread().getId(), map.get(3)));
        };

        executorService.execute(replace);
        executorService.execute(replace);
        //等待准备完成
        Thread.sleep(4000L);
        latch.countDown();
        //等待完成替换
        Thread.sleep(300L);
        System.out.println(map.get(3));
        executorService.shutdown();
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文