在像Hashtable
和Vector
这样的东西被弃用后,当集合同步包装器出现时,我认为同步会被更有效地处理。现在我查看了代码,惊讶地发现它只是用同步块包装了集合。
为什么像ReadWriteLock
这样的东西没有包含在集合中,例如在Collections的SynchronizedMap中?是否存在某些效率考虑,使其不值得呢?
在像Hashtable
和Vector
这样的东西被弃用后,当集合同步包装器出现时,我认为同步会被更有效地处理。现在我查看了代码,惊讶地发现它只是用同步块包装了集合。
为什么像ReadWriteLock
这样的东西没有包含在集合中,例如在Collections的SynchronizedMap中?是否存在某些效率考虑,使其不值得呢?
Collections.synchronizedMap
的一般用例,而不是具有主要读取器的特殊用例。
进一步链接
他们编写了一个工具,根据需要自动将Java应用程序中的锁转换为ReentrantLocks
和ReadWriteLocks
。为了测量目的,他们还提供了一些有趣的基准测试结果:
至少有一种机器/虚拟机配置(例如,请注意在使用Sun 1.5.0_15 JVM的2核机器上,ReentrantLocks是最快的)。[...] 然而,在写操作更普遍的配置中, 基于Sun 1.6.0_07 JVM的同步块版本比基于读写锁的版本快50% (基于Sun 1.5.0_15 JVM快14%)。
在只有1个读取器和1个写入器的低竞争情况下,性能差异较小, 并且三种类型的锁均可以产生最快的版本。
SynchronizedMap
中,例如。 - GrayReadWriteLock
比标准同步块更昂贵,而且优势似乎很大。如果您还有一分钟,能否详细说明成本差异?谢谢! - MiquelReadWriteLock
(如果您正在谈论的是这个)并不一定比使用synchronized
关键字更快。这两种构造都会施加锁定并建立内存屏障和“happens before”限制。Collections.synchronizedMap(...)
和相关方法中做一些聪明的事情,其中读取方法被读锁定,写入方法被写锁定以提高性能。这可能对于java.util
集合类可以很好地工作,但如果用户实现了Map
,并且get()
方法正在计数或类似的操作,则可能会导致同步问题-即一个“只读”的方法实际上对集合进行更新。是的,这样做是一个可怕的主意。
ConcurrentHashmap
被编写成高性能,并直接使用volatile
字段而不是synchronized
块。这使代码与Collections.synchronizedMap(...)
相比显着更复杂,但也更快。这就是为什么它被推荐用于高性能场景而不是Collections.synchronizedMap(new HashMap<...>())
的原因。HashTable
是在Java 1.5并发类和集合写入之前编写的。它使用方法级别的synchronized
语句,而不是使用内部互斥锁的synchronizedMap(...)
。它比HashMap
也更低效。这只是旧代码。 - GrayConcurrentHashMap
是从头开始编写的,旨在成为(顾名思义)高性能并发哈希映射。它比synchronizedMap(...)
具有更高的性能,因为锁定更加细粒度。它也更加复杂,并且具有一些方法调用(例如ConcurrentMap
方法),可以添加一些有用的并发功能。 - Gray除了以下内容,大多数推理都已经解决。SynchronizedMap/Set/List以及Hashtable和Vector依赖于集合实例在同步状态下。因此,许多开发人员使用这种同步来确保原子性。例如。
List syncList = Collections.synchronizedList(new ArrayList());
//put if absent
synchronized(syncList){
if(!syncList.contains(someObject)){
syncList.add(someObject);
}
}
这是线程安全的,并且对所有操作都是原子性的,因为synchronizedList将在自身上进行同步(即add、remove、get)。这就是Hashtable类没有改为支持锁分离类似于ConcurrentHashMap的主要原因。
因此,对于这些集合使用ReadWriteLock会失去原子操作的能力,除非您能够获取锁实例。
这是因为那些类在Java引入读/写锁之前就存在了(Java 5)。它们很少有用,因为它们的锁定方式已经被硬编码在其中,锁定粒度非常细。
这与效率无关。在 Collections.synchronizedMap
中使用 synchronized
是没有问题的。如果使用 ReadWriteLock 来实现 Map,我会称之为 Collections.LockedMap
;)
说正经的,Collections.synchronizedMap
是在 Lock
出现多年前编写的 API。它是一个发布后不能更改的 API。
补充@JohnVint的答案,考虑迭代问题。 文档明确要求客户端同步:
It is imperative that the user manually synchronize on the returned list when iterating over it:
List list = Collections.synchronizedList(new ArrayList()); ... synchronized (list) { Iterator i = list.iterator(); // Must be in synchronized block while (i.hasNext()) foo(i.next()); }
Failure to follow this advice may result in non-deterministic behavior.
这仅在客户端可以共享返回的映射上的内在锁时才起作用。如果它在内部使用读/写锁,则返回的接口必须支持访问至少读取锁以进行安全迭代的某种方式。这将为可疑的好处(如其他人所解释的那样)使API变得复杂。