ZooKeeper实现读写锁

完整代码在 https://github.com/SeemSilly/codestory/tree/master/research-zoo-keeper

1 读写锁的概念

参考维基百科的条目: https://zh.wikipedia.org/wiki/读写锁

读写锁是计算机程序的并发控制的一种同步机制,用于解决读写问题,读操作可并发重入,写操作是互斥的。 读写锁有多种读写权限的优先级策略,可以设计为读优先、写优先或不指定优先级。

读优先:允许最大并发的读操作,但可能会饿死写操作;因为写操作必须在没有任何读操作的时候才能够执行。

写优先:只要排队队列中有写操作,读操作就必须等待;

不指定优先级:对读操作和写操作不做任何优先级的假设

不指定优先级的策略,最适合使用ZooKeeper的子节点模式来实现,今天就来尝试这种策略。

2 锁设计

同前面介绍的普通分布式锁,也使用子节点模式实现。先用容器模式(CreateMode.CONTAINER)创建唯一的锁节点,每个锁客户端在锁节点下使用临时循序模式(CreateMode. SEQUENTIAL)创建子节点。这些子节点会自动在名称后面追加10位数字。

2.1 如何标识读锁还是写锁?

有两种简单的方案:在子节点名中标识、在节点的值中标识。如果采用在值中标识,每次子节点列表后,还需要再分别读一下子节点的值,才能判断是读锁还是写锁,会比较耗时。如果在子节点名称中标识,会面临一个问题:在同一个节点中创建的子节点,如果给定的名称不同,追加的10位数字是否仍然是递归的?

写个测试用例验证一下。

public class SequentialTest extends TestBase { @Test public void testSequential() throws Exception { String rootNodeName = "/container-" + System.currentTimeMillis(); ZooKeeperBase zooKeeper = new ZooKeeperBase(address); zooKeeper.createRootNode(rootNodeName, CreateMode.CONTAINER); Random random = new SecureRandom(); long lastNumber = -1L; String[] prefixs = new String[] {"/a", "/b", "/c", "/d", "/e", "/f", "/g"}; for (int i = 0; i < 10; i++) { int index = random.nextInt(prefixs.length); String childNodeName = rootNodeName + prefixs[index]; String fullNodeName = zooKeeper.getZooKeeper().create(childNodeName, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); long number = Long.parseLong(fullNodeName.substring(childNodeName.length())); assert number == lastNumber + 1; lastNumber = number; } } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zywjzp.html