同步集合是否始终需要同步块?

时间:2017-01-12 18:36:42

标签: java synchronized

考虑到:

  • Collections.synchronizedMap(new LinkedHashMap<>());
  • 主循环Thread
    • 在上面的地图上执行计划任务(来自阻止队列)。
      • 任务基本上可以用map做任何事情(也可以在里面迭代和更新值)。
  • 多个循环“迷你”主题
    • 需要不时地查找地图的当前元素(仅迭代,无版本)。

现在 - 当前是关键字 - 我需要直接流量 - 无法使用快照。

根据文档:(但我不确定我能做什么)

  

当迭代任何集合视图时,用户必须手动同步返回的地图。

我需要的是,只要地图被主线程修改或迭代,“迷你”线程都无法访问它(锁定)。

同时,当主线程未触及所述地图时 - 所有“迷你”线程都可以访问地图但是他们喜欢(但他们不会以任何方式修改它 - 只是迭代/读取)。

但是现在 - 当主线程想要修改地图而“迷你”迭代它时会发生什么?

这是否意味着我需要尽可能将地图放在同步块中?

ReadWriteLock会在这里提供帮助吗?请记住,查找“迷你”线程不能是快照。

4 个答案:

答案 0 :(得分:1)

如果您需要对Map的独占写入权限,则应进行同步。您不需要使用Collections.synchronizedMap(),只需确保始终在同一对象上进行同步:

private final Map<Whatever> myMap = new LinkedHashMap<>();

/* Your writer thread */
public void run()
{
    synchronized (myMap) {
       /* Write, iterate, whatever */
    }
}

/* Your reader thread */
public void run()
{
    synchronized (myMap) {
       /* Read stuff */
    }
}

只有一个线程可以同时为给定对象输入synchronized块。因此,您的作者线程和读者线程将永远不会在您的地图上同时工作。

确保在synchronized区块内尽可能少地完成工作,并在完成后尽快离开,以减少争用。

请记住,与Map上的方法返回的同步包装器相比,Collections(或任何对象)的同步(序列化)访问完全不同。仅仅因为您使用Map返回的Collections.synchronizedMap()并不意味着您的代码是线程安全的。这同样适用于ConcurrentHashMap。仅因为名称中有Concurrent,并不意味着您不能以线程不安全的方式使用它。

答案 1 :(得分:1)

Collections.synchronizedMap()会同步访问地图的每个方法,但是Iterator上的方法都是单独调用的,这就是必须在地图上同步的原因在迭代期间。

同步是一个独占锁,因此只有一个线程可以一次迭代地图。如果你有许多读者(迭代器)并希望它们并行执行,你肯定应该使用ReadWriteLock替换同步。并发实现对你来说不是一个可行的选择,因为你说&#34;不能使用快照&#34;。

这意味着,请勿使用LinkedHashMap包裹Collections.synchronizedMap()。相反,创建一个ReentrantReadWriteLock,并且始终获取围绕任何使用地图的锁定,使用readLock()作为读者(迭代,{{1} },get()等)和containsKey()用于mutators(writeLock()put(),与remove()的迭代等)。

答案 2 :(得分:0)

是的,您需要以某种方式进行同步,以避免在迭代时进行并发修改。

如果写入操作的频率低于读取操作,那么.mdf可能是适合您的工具。

否则,ReadWriteLock - 块可能是实现此目的的最简单方法。

答案 3 :(得分:0)

  

这是否意味着我需要尽可能将地图放在同步块中?

不是在任何可能的情况下,但仅在您在该集合上获得迭代器时。 因为除了iterator()之外,当您使用synchronizedMap()API请求同步集合时,所有其他方法都会为您同步。

例如如果要获取集合的大小,则不需要在synchronized块中调用yourmap.size(),因为装饰器会为您处理同步。

您可以参考Collections.java进一步参考。