当前位置: 首页 > 面试题库 >

Java Hashmap-多线程放置

仲孙景胜
2023-03-14
问题内容

我们最近在工作中讨论了在多线程环境中是需要使用ConcurrentHashMap还是可以简单地使用常规HashMap。HashMaps的参数有两个:它比ConcurrentHashMap更快,所以我们应该尽可能使用它。而且ConcurrentModificationException显然只有在修改后迭代地图时才会出现,所以“如果我们仅从地图进行PUT和GET,则常规HashMap有什么问题?”
是争论。

我以为并发PUT操作或并发PUT和READ可能导致异常,因此我进行了测试以证明这一点。测试很简单;创建10个线程,每个线程一次又一次地将相同的1000个键值对再次写入映射,持续5秒钟,然后打印结果映射。

结果实际上令人困惑:

Length:1299
Errors recorded: 0

我以为每个键值对在HashMap中都是唯一的,但是从地图上看,我可以找到多个相同的键值对。 我期望某种异常或损坏的键或值,但是我没有想到这一点。
这是怎么发生的?

这是我使用的代码,仅供参考:

public class ConcurrentErrorTest
{
    static final long runtime = 5000;
    static final AtomicInteger errCount = new AtomicInteger();
    static final int count = 10;

    public static void main(String[] args) throws InterruptedException
    {
        List<Thread> threads = new LinkedList<>();
        final Map<String, Integer> map = getMap();

        for (int i = 0; i < count; i++)
        {
            Thread t = getThread(map);
            threads.add(t);
            t.start();
        }

        for (int i = 0; i < count; i++)
        {
            threads.get(i).join(runtime + 1000);
        }

        for (String s : map.keySet())
        {
            System.out.println(s + " " + map.get(s));
        }
        System.out.println("Length:" + map.size());
        System.out.println("Errors recorded: " + errCount.get());
    }

    private static Map<String, Integer> getMap()
    {
        Map<String, Integer> map = new HashMap<>();
        return map;
    }

    private static Map<String, Integer> getConcMap()
    {
        Map<String, Integer> map = new ConcurrentHashMap<>();
        return map;
    }

    private static Thread getThread(final Map<String, Integer> map)
    {
        return new Thread(new Runnable() {
            @Override
            public void run()
            {
                long start = System.currentTimeMillis();
                long now = start;
                while (now - start < runtime)
                {
                    try
                    {
                        for (int i = 0; i < 1000; i++)
                            map.put("i=" + i, i);
                        now = System.currentTimeMillis();
                    }
                    catch (Exception e)
                    {
                        System.out.println("P - Error occured: " + e.toString());
                        errCount.incrementAndGet();
                    }
                }
            }
        });
    }
}

问题答案:

您面临的问题似乎是TOCTTOU类问题。(是的,这种错误经常发生,它有自己的名字。

将条目插入地图时, 至少 需要发生以下两件事:

  1. 检查密钥是否已经存在。
  2. 如果检查返回true,则更新现有条目,否则,添加新条目。

如果这两个原子不是原子发生的(就像在正确同步的地图实现中那样),那么几个线程可以得出结论,在步骤1中该键尚不存在,但是到它们到达步骤2时,不再是真的。因此,多个线程将愉快地插入具有相同键的条目。

请注意,这不是唯一可能发生的问题,并且取决于实现方式和您的可见性,您可能会遇到各种不同的意外故障。



 类似资料:
  • 我的Java代码有问题。我想在一个键中存储多个值,但我想灵活地存储它们。这意味着我从一个文本文件中读取,每行是一个单词。为了储存它们,我想建立成对的单词。例如: 字1/字2 单词2/单词3 单词3/单词4 我已经稍微改变了这种方法。我想将键的值存储在数组列表中。这意味着每次出现新键时都会存储一个新的 Arraylist 和键,但如果键在映射中,我想将它们存储在这个键的列表中。这可能吗? 我们必须将

  • 问题内容: 我们是否可以用一个键和两个值实现HashMap。就像HashMap一样? 还请告诉我(如果没有办法)通过其他任何方法来实现三个值的存储(以一个为键)的方法,对我有帮助吗? 问题答案: 你可以: 使用具有列表作为值的地图。 创建一个新的包装器类,并将该包装器的实例放置在地图中。。 使用类似类的元组(节省创建许多包装器)。。 并排使用多个地图。 例子 1.使用列表作为值进行映射 这种方法的

  • typora-copy-images-to: img 1. 多线程概述 人们在日常生活中,很多事情都是可以同时进行的。例如,一个人可以一边听音乐,一边打扫房间,可以一边吃饭,一边看电视。在使用计算机时,很多任务也是可以同时进行的。例如,可以一边浏览网页,一边打印文档,还可以一边聊天,一边复制文件等。计算机这种能够同时完成多项任务的技术,就是多线程技术。Java是支持多线程的语言之一,它内置了对多线

  • 使用 [Web Workers][web-workers]可以在系统级线程中运行JavaScript。 多线程Node.js 在 webPreferences中将 n​​odeIntegrationInWorker选项设置为 true,可以在Electron的Web Workers中使用Node.js功能: 1 let win = new BrowserWindow({ 2 webPreferen

  • If you use Casbin in a multi-threading manner, you can use the synchronized wrapper of the Casbin enforcer: https://github.com/casbin/casbin/blob/master/enforcer_synced.go (GoLang) and https://github.

  • 多线程 Swift多线程编程方案 Thread Cocoa Operation (Operation 和 OperationQueue) Grand Central Dispath (GCD) 1. Thread在三种多线程技术中是最轻量级的, 但需要自己管理线程的生命周期和线程同步. 线程同步对数据的加锁会有一定的系统开销. detachNewThread(_ block: @escaping