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

Java 同步集合是否始终需要同步块?,java,synchronized,Java,Synchronized,考虑到: Collections.synchronizedMap(新LinkedHashMap()) 主循环线程 在上面的映射上执行计划任务(来自阻塞队列)。 任务基本上可以是您可以使用map执行的任何操作(也可以迭代和更新其中的值) 多个循环“迷你”线程 需要不时在地图的当前元素上查找(仅迭代,无编辑) 现在-current是这里的关键字-我需要直接流-不能使用快照 根据文件:(但我不确定我能做什么) 当用户迭代任何集合视图时,必须手动同步返回的映射 我确切需要的是,每当主线程

考虑到:


  • Collections.synchronizedMap(新LinkedHashMap())
  • 主循环
    线程
    • 在上面的映射上执行计划任务(来自阻塞队列)。
      • 任务基本上可以是您可以使用map执行的任何操作(也可以迭代和更新其中的值)
  • 多个循环“迷你”线程
    • 需要不时在地图的当前元素上查找(仅迭代,无编辑)
现在-current是这里的关键字-我需要直接流-不能使用快照

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

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

我确切需要的是,每当主线程对map进行修改或迭代时,没有一个“迷你”线程可以访问它(锁定)

同时,当主线程不接触所述映射时,所有“迷你”线程都可以随意访问该映射(但它们不会以任何方式修改它-只是迭代/读取)

但现在-当主线程想要修改映射,而“mini”在其上迭代时会发生什么

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


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

如果您需要对
映射的独占写入访问权,则应同步。您不需要使用集合。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 */
    }
}
private final Map myMap=new LinkedHashMap();
/*你的书写线程*/
公开募捐
{
已同步(myMap){
/*写,迭代,随便什么*/
}
}
/*你的读者线程*/
公开募捐
{
已同步(myMap){
/*阅读材料*/
}
}
只有一个线程可以同时为给定对象输入
同步的
块。因此,您的写入线程和读取线程将永远不会同时在地图上工作

确保在
synchronized
块中尽可能少地执行工作,并在完成后立即将其保留,以减少争用


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

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

如果写操作的频率远低于读操作,那么
ReadWriteLock
可能是适合您的工具

否则,
synchronized
-块可能是实现这一点的最简单方法

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

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

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


您可以参考以获取进一步的参考。

集合。synchronizedMap()
将同步对映射的每个方法的访问,但是,
迭代器上的方法都是单独调用的,这就是为什么在迭代期间必须在映射上同步的原因

同步是一个独占锁,因此一次只能有一个线程迭代映射。如果您有许多读卡器(迭代器),并且希望它们并行执行,那么您肯定应该使用。并发实现对您来说不是一个可行的选择,因为您说过“不能使用快照”


这意味着,不要使用
Collections.synchronizedMap()
包装
LinkedHashMap
。相反,创建一个映射,并始终围绕映射的任何使用获取锁,为读卡器使用
readLock()
(迭代,
get()
containsKey()
,等等),并为变体使用
writeLock()
put()
remove()
,使用
remove()
进行迭代,等等).

当您忙于查看并发包时,您忘记了基本解决方案。请随意使用
,但同步可能就足够了。如果发现存在大量争用,可以切换到另一种并发控制机制。
Collections.synchronizedMap()
只是一种使用
synchronized
块“自动”包装映射上所有方法的方法。不使用集合。synchronizedMap()
只是意味着你必须自己做,这意味着:1)更多的代码。2) 容易出错(您可能会忘记执行此操作)因此,我认为这个答案并不是这个问题的好答案。同样的缺点也适用于使用RWL。在
Map
操作上手动同步与使用方法同步的
Map
不同。的确,使用
ReadWriteLock
您没有太多选择,但您的答案根本不是关于RWL,而是提倡手动
synchronized
块而不是使用
synchronizedMap()
,这就是为什么我认为答案是不好的。关于ReentrantReadWriteLock,我使用了它