正如这个问题的几个答案中所建议的那样:
What is the name of this locking technique?
我实现了一个ReentrantReadWriteLock并且看到了一个很好的加速(我知道在我的一个类中有一些锁争用并且使用重入锁确实有助于加快速度)。
但现在我想知道:如果在一个类中,所有访问(包括读取和写入)都是通过首先锁定读锁定或写锁定来完成的,这是否意味着 synchronized 关键字不应该再在那个班级使用了吗?
例如,这是在http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html
找到的一个官方Java 1.6示例class RWDictionary {
private final Map<String, Data> m = new TreeMap<String, Data>();
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
public Data get(String key) {
r.lock();
try { return m.get(key); }
finally { r.unlock(); }
}
public String[] allKeys() {
r.lock();
try { return m.keySet().toArray(); }
finally { r.unlock(); }
}
public Data put(String key, Data value) {
w.lock();
try { return m.put(key, value); }
finally { w.unlock(); }
}
public void clear() {
w.lock();
try { m.clear(); }
finally { w.unlock(); }
}
}
没有 synchronize 关键字。
现在我意识到这种锁的一个要点是比其他方法更快(在这种情况下比 synchronize 更快)但是这背后的技术解释是什么?
在每个get / update方法的类中使用读写锁是否“替换”这些方法的 synchronize 关键字?
答案 0 :(得分:11)
如果您读取了ReadWriteLock和Lock上的javadoc,他们明确指出Locks必须提供与synchronized
关键字相同的内存语义:
所有Lock实现必须强制执行内置监视器锁提供的相同内存同步语义,如Java语言规范,第三版(17.4内存模型)中所述:
(那是来自Lock javadoc; ReadWriteLock引用Lock来描述它的语义。)
所以,是的,它取代了synchronized
关键字。实际上,你可以用一个Lock替换你代码中的每个synchronized
块并具有相同的语义(并且,取决于jvm,甚至可能是一个小的性能提升。)但是你要交易一点速度更多的冗长,如果你忘记解锁其中一个锁,你可能会使你的程序陷入僵局。
非阻塞算法(compare-and-swap是其中的核心)与volatile
字段的内存语义相结合,其中指定了如果您写入volatile
字段,随后读取该字段的任何线程必须至少看到您编写时看到的世界相同的状态。 (他们也可以看到写完之后发生的部分或全部内容。)这些工具可以制作一些非常快速的代码,但它们也很微妙且容易出错 - 几乎总是最好留在更高级别构造(例如您正在使用的ReadWriteLock)。