在Java中使用HashMap时,有哪些不是线程安全的案例?

时间:2014-05-22 14:04:46

标签: java multithreading

在API文档中,我们可以看到:

If multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it must be
synchronized externally. (A structural modification is any operation
that adds or deletes one or more mappings; merely changing the value
 associated with a key that an instance already contains is not a
 structural modification.)

我在想"放"方法应该同步?它只说结构修改。你能为HashMap提供一些不安全的案例吗?当我查看" HashTable"的源代码时," get"方法也已同步,为什么不只是同步写操作?

3 个答案:

答案 0 :(得分:3)

一般的经验法则是:

如果多个线程访问集合并且至少有一个线程在某个时刻修改集合,则需要同步所有访问到集合。

如果你考虑一下,它非常明确:如果一个集合被修改另一个线程从中读取(例如迭代),那么读写操作可能会相互干扰(读取部分写入,例如创建条目但尚未设置值或条目尚未正确链接)。

免除这个是一个线程创建和修改的集合,然后是“世界”的手,但在发布它们的引用后从不修改它们。

答案 1 :(得分:1)

一个例子是:

主题1:

Iterator<YourType> it = yourMapInstance.entrySet().iterator();
  while(it.hasNext()) {
    it.next().getValue().doSth();
    Thread.sleep(1000);
  }
}

主题2:

for(int i = 0; i < 10; i++) {
  if(Math.random() < 0.5) {
    yourMapInstance.clear();
    Thread.sleep(500);
  }
}

现在,如果两个线程同时执行,在某些时候可能存在一种情况,即迭代器中有一个值,而另一个线程已经从地图中删除了所有内容。在这种情况下,需要同步。

答案 2 :(得分:1)

  

为什么不只是同步写操作?

如果读取也未同步,则可能会遇到可见性问题。不仅如此,如果对象执行结构更改,也可以完全 thrash 对象!

JVM规范提供了一些保证,确保一个线程对内存位置的修改何时对其他线程可见。一个这样的保证是,在释放锁之前线程的修改对随后获取相同锁的线程可见。这就是为什么你需要同步读取操作,即使没有对象的并发结构修改。

请注意,此释放/获取锁定不是保证内存修改可见性的唯一方法,但它是最简单的方法。其他包括启动线程的顺序,类初始化,对内存位置的读/写......更复杂的东西(由于争用级别降低,可能在高度并发的环境中更具可伸缩性)。

如果您不使用任何其他技术来确保可见性,那么仅仅在写操作上锁定就是错误的代码。您可能会遇到也可能不会遇到可见性问题 - 无法保证JVM 将失败,但这是可能的,所以......错误的代码。

我建议你阅读JVM规范本身之后的“Java Concurrency in Practice”这本书,这是我读过的最好的文章之一。显然,这本书更容易(仍然远非琐碎!),阅读比规范更有趣......

相关问题