Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在多线程环境中使用HashMap_Java_Multithreading - Fatal编程技术网

Java 在多线程环境中使用HashMap

Java 在多线程环境中使用HashMap,java,multithreading,Java,Multithreading,我正在回答一个面试问题,我很难理解这个问题: 在多线程环境中使用HashMap有什么问题?get()方法何时进入无限循环 在我看来,在多线程环境中使用HashMap不是问题,只要我们的应用程序不访问/读取修改创建的HashMap的线程,而不是简单地访问HashMap 因此,在我看来,只要在应用程序中我们只是在多线程环境中访问HashMap,就没有问题 请告诉我我的理解是否正确。这是一个经典问题。ArrayList和HashMap不同步,而Vector和HashTable不同步。因此,除非您自己非

我正在回答一个面试问题,我很难理解这个问题:

在多线程环境中使用HashMap有什么问题?get()方法何时进入无限循环

在我看来,在多线程环境中使用
HashMap
不是问题,只要我们的应用程序不访问/读取修改创建的
HashMap
的线程,而不是简单地访问HashMap

因此,在我看来,只要在应用程序中我们只是在多线程环境中访问
HashMap
,就没有问题


请告诉我我的理解是否正确。

这是一个经典问题。ArrayList和HashMap不同步,而Vector和HashTable不同步。因此,除非您自己非常小心地定义互斥体,否则应该使用哈希表

换句话说,例如HashTable中的方法将确保在任何给定时间都没有其他线程使用HashTable。如果使用HashMap,则必须在调用该方法之前确保在HashMap上进行同步,从而手动执行此操作

更新:checkout@Gray的评论。它看起来像是用集合包装HashMap。synchronizedMap(新的HashMap())是现在的做法

编辑:其他海报的回答比我好。然而,我的回答引发了一场关于即将弃用的Vector、Stack、Hashtable和Dictionary类使用的有趣讨论,因此我将问题留在这里,作为下面评论的开头。谢谢大家

在多线程环境中使用HashMap有什么问题?get()方法何时转到无限循环

让多个线程以不受保护的方式使用非同步集合(实际上是任何可变类)是一个错误。如果每个线程都有自己的
HashMap
实例,那么这不是问题。如果多个线程添加到同一个
HashMap
实例时未对其进行
同步
,则会出现问题。即使只有一个线程在修改
HashMap
,而其他线程在没有同步的情况下从同一映射中读取,您也会遇到问题

如果您需要在多个线程中使用同一个哈希表对象,那么您应该考虑使用<代码> CONCURNESHASMAP ,将每个访问打包到<代码> HashMap 中,在<代码>同步{} /代码>块中,或者使用<代码>集合。 很有可能

get()
进入无限循环,因为其中一个线程在内存中只有
HashMap
的部分更新视图,并且必须存在某种对象引用循环。这就是在多线程中使用非同步集合的危险所在

所以在我看来,只要在应用程序中我们只是在多线程环境中访问HashMap,这就不是问题了

如果“访问”的意思是“阅读”,那么这是正确的,有资格的。您必须确保:

  • HashMap
    的所有更新都在线程实例化之前完成,创建映射的线程也会分叉线程
  • 线程仅在只读模式下使用
    HashMap
    get()
    或不删除的迭代
  • 没有线程更新映射

如果这些条件中的任何一个都不正确,那么您将需要使用同步映射来代替。

我猜他们的意思是访问
HashMap
的共享副本<代码>共享可变状态

因为它不是
同步的
,所以每个线程都会从主存中获取它的副本,修改并覆盖它

HashMap with one entry <n, 1>

thread 1 grab the copy

thread 2 grab the copy

thread 1 modify <n, 2>

thread 2 modify <n, 3>

thread 1 is done, and stores the copy in the main memory

now memory is <n, 2>

thread 2 is done and stores the copy

now memory is <n, 3>

The state thread 1 is lost
带有一个条目的HashMap 线程1获取副本 线程2获取副本 线程1修改 线程2修改 线程1已完成,并将副本存储在主内存中 现在记忆是 线程2完成并存储副本 现在记忆是 状态线程1丢失
我们知道
HashMap
是一个非同步的集合,而它的同步计数器部分是
HashTable
。因此,当您在多线程环境中访问集合并且所有线程都在访问集合的单个实例时,出于各种明显的原因(例如避免脏读和保持数据一致性),使用
哈希表
更安全。在最坏的情况下,这种多线程环境也会导致无限循环

是的,这是真的
HashMap.get()
可能导致无限循环。让我们看看如何

如果查看源代码
HashMap.get(对象键)
方法,它如下所示:

 public Object get(Object key) {
    Object k = maskNull(key);
    int hash = hash(k);
    int i = indexFor(hash, table.length);
    Entry e = table[i];
    while (true) {
        if (e == null)
            return e;
        if (e.hash == hash &amp;&amp; eq(k, e.key))
            return e.value;
        e = e.next;
    }
}
while(true){…}
在运行时总是一个无限循环的牺牲品 多线程环境,如果有的话,e.next可以指向它自己。这将导致无限循环。但是,e.next将如何指向自己

这可能发生在
void transfer(Entry[]newTable)
方法中,该方法在HashMap调整大小时调用

    do {
        Entry next = e.next;
        int i = indexFor(e.hash, newCapacity);
        e.next = newTable[i];
    newTable[i] = e;
        e
= next;
} while (e != null);
如果调整大小的同时,其他线程试图修改map实例,那么这段代码很容易产生上述情况


避免这种情况的唯一方法是在代码中使用同步,或者更好地使用同步的集合。

哈希表
几乎已被弃用,不应使用。如果您需要并发HashMap,那么您应该使用ConcurrentHashMap或集合.synchronizedMap(new HashMap())@Gray哦,这很有趣,我不知道。你提到过那件事吗?由于HashTable没有被标记为不推荐使用,这让人感到意外。谢谢@Gray,既然我们已经这么做了,vector也不推荐了吗?@Gary或者使用ConcurrentHashMap,它可以在没有CME的情况下安全地进行迭代。它们是“不推荐的”