为什么hashmap不是线程安全的?

时间:2018-09-27 05:47:17

标签: java

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;


public class TestLock {
    private static ExecutorService executor = Executors.newCachedThreadPool();
    private static Map<Integer, Integer> map = new HashMap<>(1000000);
    private static CountDownLatch doneSignal = new CountDownLatch(1000);

    public static void main(String[] args) throws Exception {

        for (int i = 0; i < 1000; i++) {
            final int j = i;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    map.put(j, j);
                    doneSignal.countDown();
                }
            });
        }
        doneSignal.await();
        System.out.println("done,size:" + map.size());
    }
}

有人说并发插入哈希表并不安全。因为哈希图将执行容量扩展操作,但是我在此处将大小设置为1000000,它将仅扩展为750,000。我在这里做1000次插入,所以我不会扩展它。因此应该没有问题。但是结果总是小于1000,出了什么问题?

5 个答案:

答案 0 :(得分:4)

  

为什么哈希图不是线程安全的?

因为javadocs这么说。见下文。

您说:

  

有人说并发插入哈希表并不安全。

不仅仅是“某些人” 1 javadocs明确指出:

  

请注意,此实现未同步。。如果多个线程同时访问哈希映射,并且至少一个线程在结构上修改了该映射,则必须在外部进行同步。(结构修改是添加或删除一个或多个映射的任何操作;仅更改与实例已包含的键相关联的值不是结构上的修改。“

您问:

  

我在这里进行1000次插入,因此不会扩展它。因此应该没有问题。但是结果总是小于1000,出了什么问题?

这不仅仅是散列数组的扩展。不只是插入。在HashMap上执行结构修改的任何操作都需要同步...否则您可能会得到未指定的行为。

这就是你所得到的。


1-我强烈建议您不要依赖直觉或“某些人”的说法。相反,请花些时间阅读并了解相关规范。即Javadocs和Java语言规范。

答案 1 :(得分:2)

“因为哈希图将执行容量扩展操作”不仅是HashMap线程不安全的原因。

您必须参考Java内存模型以了解其可以提供的保证。

此类保证之一是可见性。这意味着除非满足特定条件,否则一个线程中的更改可能在其他线程中不可见。

答案 2 :(得分:1)

“问题”标题并没有真正描述您的要求。反正

在这里,您已将“容量”设置为1000000。不是大小。

  

容量:最初在此哈希图中要包含多少个插槽。基本上   空插槽。

     

大小:地图中填充的元素数。

因此,即使将容量设置为1000000,结尾处的元素也不多。因此,将通过topics = list() t = NewTopic(topic, num_partitions=3, replication_factor=1, config={'log.retention.hours': '168'}) topics.append(t) # Call create_topics to asynchronously create topics, a dict # of <topic,future> is returned. fs = a.create_topics(topics) # Wait for operation to finish. # Timeouts are preferably controlled by passing request_timeout=15.0 # to the create_topics() call. # All futures will finish at the same time. for topic, f in fs.items(): try: f.result() # The result itself is None print("Topic {} created".format(topic)) except Exception as e: print("Failed to create topic {}: {}".format(topic, e)) 方法返回映射中填充的元素数。它与并发问题无关。是的,由于多种原因,HashMap不是线程安全的。

答案 3 :(得分:0)

如果您在HashMap类here中看到'put'的实现,则在'synchronize'处不使用,尽管它执行了许多线程不安全的操作,例如如果未找到密钥的哈希值,则创建TreeNode,请增加modCount等等

ConcurrentHashMap将适合您的用例

答案 4 :(得分:-4)

如果您需要线程安全的HashMap,则可以改用Hashtable类。

  

与新的集合实现不同,Hashtable是同步的。关于Hashtable的javadoc说,如果不需要线程安全的实现,建议使用HashMap代替Hashtable。

同样,如果一天需要线程安全的ArrayList,请使用Vector

编辑:哦,我建议使用错误的方法。我的应用程序! 评论建议比我更好的解决方案: Collections.synchronizedXxx()ConcurrentHashMap,这是这个问题的开场白。