在不鼓励Hashtable
和Vector
之类的内容之后,当Collections同步包装器出现时,我认为可以更有效地处理同步。现在我查看了代码,我很惊讶它只是用同步块包装集合。
为什么ReadWriteLock
未包含在集合中的SynchronizedMap中?是否存在一些不值得的效率考虑因素?
答案 0 :(得分:11)
读写锁是性能优化的一部分,这意味着它可以在某些情况下允许更大的并发性。必要条件是,它们适用于大部分时间读取但未修改的数据结构。
在其他条件下,它们的表现略差于排他性锁,这很自然,因为它们具有更大的复杂性。
如果读写锁的锁通常保持适度长时间并且对受保护资源只进行少量修改,则效率最高。
因此,读写锁是否优于排它锁取决于用例。最终,您必须使用分析来衡量哪些锁具有更好的性能。
考虑到这一点,选择Collections.synchronizedMap
解决一般用例的排他锁而不是大多数读者的特殊情况似乎是合适的。
更多链接
Refactoring Java Programs for Flexible Locking
他们编写了一个工具,可以在适当的情况下自动将Java应用程序中的锁转换为ReentrantLocks
和ReadWriteLocks
。出于测量目的,他们还提供了一些有趣的基准测试结果:
[...]然而,在写作操作更多的配置中 流行的,同步块的版本比50%快 一个基于Sun 1.6.0_07 JVM的读写锁(快14%) 与Sun 1.5.0_15 JVM)。
在只有1的低争用情况下 读者和1位作家,性能差异不那么极端, 并且三种类型的锁中的每一种都产生了最快的版本 至少一台机器/ VM配置(例如,注意在使用Sun 1.5.0_15 JVM的2核机器上ReentrantLocks最快)。
答案 1 :(得分:6)
我认为使用ReadWriteLock
(如果这就是你所说的话)并不比使用synchronized
关键字更快。这两种结构都强加了锁定并建立了记忆障碍,并且“在之前发生”限制。
您可能正在讨论在Collections.synchronizedMap(...)
和朋友中做一些聪明的事情,其中读取方法被读取锁定并且写入方法写入锁定以提高性能。这可能适用于java.util
集合类,但如果计算Map
方法或其他东西,可能会导致用户实现get()
的同步问题 - 即某个方法是“只读“实际上对集合进行了更新。是的,这样做会是一个糟糕的主意。
ConcurrentHashmap
编写为高性能,直接使用volatile
字段而不是synchronized
块。与Collections.synchronizedMap(...)
相比,这使得代码更加复杂,但也更快。这就是推荐用于Collections.synchronizedMap(new HashMap<...>())
以上的高性能情况的原因。
答案 2 :(得分:5)
除了以下内容之外,大多数推理都已得到解决。 SynchronizedMap / Set / List以及Hashtable和Vector依赖于同步的集合实例。因此,许多开发人员使用此同步来确保原子性。例如。
List syncList = Collections.synchronizedList(new ArrayList());
//put if absent
synchronized(syncList){
if(!syncList.contains(someObject)){
syncList.add(someObject);
}
}
这是所有操作的线程安全和原子,因为synchronizedList将自身同步(即添加,删除,获取)。这就是为什么Hashtable类没有被改装以支持类似于ConcurrentHashMap的锁定条带化的主要原因。
因此,除非您能够获取锁实例,否则对这些集合使用ReadWriteLock将失去原子操作的能力。
答案 3 :(得分:3)
这是因为这些类早于读/写锁,后来很晚才进入Java(Java 5)。无论如何,由于它们采用硬编码的细粒度锁定,它们很少有用。
答案 4 :(得分:0)
这与效率无关。在synchronized
中使用Collections.synchronizedMap
并没有错。如果使用ReadWriteLock来实现Map,我想称之为Collections.LockedMap
;)
要认真,Collections.synchronizedMap
是在Lock
出现之前写的。这是一个在发布后无法更改的API。
答案 5 :(得分:0)
要添加到@JohnVint's answer,请考虑迭代问题。 documentation明确要求客户端同步:
用户必须手动同步返回的内容 迭代时列出:
List list = Collections.synchronizedList(new ArrayList()); ... synchronized (list) { Iterator i = list.iterator(); // Must be in synchronized block while (i.hasNext()) foo(i.next()); }
不遵循此建议可能会导致非确定性行为。
这只能起作用,因为客户端可以在返回的地图上共享内在锁。如果要在内部使用读/写锁,则返回的接口必须支持某种方式至少访问读锁以进行安全迭代。这可能会使API变得更加复杂(正如其他人所解释的那样)。