Java 写时复制与直接锁定/写时同步方法有何不同?
写时复制被认为是并发场景中的良好实践之一。但是,我不清楚它与写方法上的简单锁定/同步有什么不同。有人能解释一下吗 书面复制:Java 写时复制与直接锁定/写时同步方法有何不同?,java,multithreading,concurrency,Java,Multithreading,Concurrency,写时复制被认为是并发场景中的良好实践之一。但是,我不清楚它与写方法上的简单锁定/同步有什么不同。有人能解释一下吗 书面复制: public V put(K key, V value) { synchronized (this) { Map<K, V> newMap = new HashMap<K, V>(internalMap); V val = newMap.put(key, value);
public V put(K key, V value) {
synchronized (this) {
Map<K, V> newMap = new HashMap<K, V>(internalMap);
V val = newMap.put(key, value);
internalMap = newMap;
return val;
}
}
对于写线程,在上述两个示例中,它们是相互排除的,相同
对于读线程,在写时复制中,运行“internalMap=newMap”后的读操作将获得更新的值。在直接锁中,运行“internalMap.put(key,value)”之后的读取操作将获得更新的值,大致相同
那么,我们为什么要推广书面复制呢?为什么我们在编写时必须“复制”?本例中的一个好处是,您可以获得写时复制案例的快照语义:对
internalMap
的每个引用都是不可变的,一旦获得,就不会再更改。当您有许多并发读取操作遍历internalMap
,并且只有偶尔的更新时,这会非常有用 使用锁和写时复制(实际上)实现了相同的功能。它们中没有一个天生比另一个更好
一般来说,当有大量读取,但很少写入时,即写即复制性能更好。这是因为平均而言,读取比使用锁时更便宜,而写入由于复制而更昂贵。当您有大量写入时,通常最好使用锁
为什么写东西更贵可能是显而易见的(你必须在每次写东西时复制整个地图,duh)。价格便宜的原因如下:
volatile Map<K, V> internalMap = new HashMap<>();
volatile Map internalMap=new HashMap();
读取internalMap不需要获取锁(有关更多详细信息,请参阅)。一旦线程获得了对internalMap
的引用,它们就可以继续处理该副本(例如,遍历条目),而无需与其他线程协调,因为可以保证它不会发生变异。根据需要,可以使用多个线程创建映射的单个副本(快照)
通过类比来解释,假设一位作者正在起草一篇文章,他们有几个人作为他们的事实核查员。有了锁,他们中只有一个人可以在草稿上工作。通过写时复制,作者将一个不可变的快照(副本)发布到某个地方,事实核查人员可以抓取该快照并执行其工作-在他们执行工作时,他们可以根据需要读取快照(而不是每次忘记文章的某个部分时都打断作者)
Java的锁多年来有所改进,因此差异很小,但在极端情况下,不必获取锁/不必在线程之间协调可能会导致更高的吞吐量等。这个示例是从哪里获得的?还是你自己想出来的?至少有一个好处是可以不锁定地进行迭代。
volatile Map<K, V> internalMap = new HashMap<>();