ReentrantReadWriteLock
非常适合基于接收时以编程方式编程方式的时间戳的读写方案。
PUT(KEY=1,VALUE=1)
PUT(KEY=1,VALUE=2)
GET(KEY=1)
PUT(KEY=1,VALUE=1)
...
Java ReentrantReadWriteLock
将根据 Java 本身提供的时间戳按顺序自动同步所有这些。但是,我需要如何使用与每个请求一起提供的外部时间戳。
PUT(KEY=1,VALUE=1,TIMESTAMP=13000000000000)
PUT(KEY=1,VALUE=2,TIMESTAMP=13500000000000)
GET(KEY=1,TIMESTAMP=14000000000000)
PUT(KEY=1,VALUE=1,TIMESTAMP=15000000000000)
...
如何设计按外部时间戳排序的读写锁?
简短回答:
同步接收时间戳数据的通道,或在接收端使用有序并发数据结构(如ConcurrentSkipListMap
)。同时质疑您是否需要保持此顺序。
附言 ReentrantReadWriteLock
不使用时间戳等可比较实体来建立排序,其公平调度算法不包括对可重用的条目进行重新排序。 ReentrantReadWriteLock
通过AbstractQueuedSynchronizer
使用基于 CLH 的锁定队列。
长答案:
虽然你想做的事情很可能是你无法避免的事情,但质疑你是否真的需要在并发和/或分布式系统中具有各种精度和一致性总是好的。
你为什么关心这个问题?
听起来您希望通过使用系统中另一层的数据排序来保持公平性。您是否需要将这两层分开,也许是因为其中一层不受您的控制,或者因为它们需要在语义上保持分离?如果是这种情况,你可以在这里再问自己几个问题。
是否绝对有必要为每个请求维护此顺序?
此排序是业务逻辑的关键部分吗?它实际上是由您关心的上层组件建立的吗?
- 如果它基于某些请求通过您无法控制的网络(如 Internet)到达的时间,则很可能您并不真正关心此排序,并且放宽一致性要求可能会导致更高的吞吐量。在高度并发、不公平的环境中,不公平的流离失所请求比在资源利用有问题的公平环境中的请求得到更快的服务,这种情况并不少见。
- 如果它基于一个单一的、超快速的时间戳颁发者,该颁发者在所有请求上建立总顺序,则可以修改系统,以便时间戳颁发者成为通过中断器或 ArrayBlockingQueue 将请求提供给系统第二层的单个生产者。
您真的会收到乱序请求吗?
你可能正在解决一个你永远不会真正面对的问题,或者你将在遥远的未来某个地方面对的问题,与此同时,你的时间可以更好地花在其他地方。
如果不是这种情况,并且您实际上希望接收无序(以外部时间戳顺序)请求,那么层之间的通信通道是在系统中引入"无序"的组件之一。这可能是因为有人故意想用一致性换取吞吐量,也可能是因为该通道不适合您的系统需求,而无需额外的工作。
考虑是更容易在其中强制实施严格的一致性,还是更容易保持原样并在收到请求后对请求进行排序。
- 我们已经通过为SingleProducerTimestampIssuer™说明的方法触及了前者 - 如果您无法控制通道,它也可能是不可行的。
- 对于后者,您可以尝试使用有序并发数据结构。将时间戳映射到请求
ConcurrentSkipListMap
可能是一个很好的解决方案。如果你不怕尝试应用论文中的想法,你可能想看看 无锁的并发编程和快速并发数据结构显式时间戳