我们用 Clojure 来做一个 "规则引擎"。每个函数代表一条规则,元数据描述了这个规则并提供了一些针对该规则的静态配置。系统是不可变的且是并发的。
如果有两个或更多线程同时调用同一个 Var,由于 AReference#meta() 是同步的,它们会导致相互阻塞(见附图,红色点)。
(defn
^{:rule {:remote-address "127.0.0.1"}}
example
[request]
(let [rule (:rule (meta #'example))]
(= (:remote-address rule) (:remote-address request))))
*解决方案:* 用 rwlock 替换 synchronized 块以实现更高的读并发性。这种方法消除了元数据读的竞争(见注释中的实际例子)。然而,也存在以下缺点:
* 每个 AReference(所有命名空间、vars、atoms、refs 和 agents)都增加了额外的字段
* 将锁的构造添加到 AReference 的构造中(影响性能和启动时间)
* 补丁:clj-1888-2.patch 将 synchronized 替换为 rwlock 以实现更高的读并发性
* 其他方案:*
* 对于 _meta 使用 volatile,对于 alter/reset 使用 synchronized。允许在 volatile 下方读取 _meta,这会足够安全吗?
* 将 AReference 从 ReentrantReadWriteLock 继承而不是持有它——这是相当奇怪的,但对于内存/构建可能会有不同的(可能是更好的)影响。