架构师课程的分布式锁,在我看来有问题呀,老师您看看呢
来源:9-5 开发分布式锁

Panda_io
2021-03-02
老师好呀,我从架构师课程过来的,架构师老师讲分布式锁的时候我觉得有些问题,想这边请教下最帅气的风间老师
先上代码(代码来自架构师课程的源码)
Slf4j
public class ZkLock implements AutoCloseable , Watcher {
private ZooKeeper zooKeeper;
private String znode;
public ZkLock() throws IOException {
this.zooKeeper = new ZooKeeper("192.168.179.106:2181",
30000,this);
}
public boolean getLock(String businessCode) {
try {
System.out.println(Thread.currentThread().getName());
// 创建业务根节点
Stat stat = zooKeeper.exists("/" + businessCode, false);
if (stat == null) {
zooKeeper.create("/" + businessCode,
businessCode.getBytes(StandardCharsets.UTF_8), ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
}
// 创建瞬时有序节点/order/order_0000001
znode = zooKeeper.create("/" + businessCode + "/" + businessCode + "_",
businessCode.getBytes(StandardCharsets.UTF_8), ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
// 获取业务节点下面的所有子节点
List<String> childrenNodes = zooKeeper.getChildren("/" + businessCode,false);
// 子节点按升序进行排序
Collections.sort(childrenNodes);
//获取序号最小的(第一个)子节点
String firstNode = childrenNodes.get(0);
// 如果创建的节点是第一个子节点,则获得锁
if (znode.endsWith(firstNode)) {
return true;
}
// 不是第一个子节点则监听前一个节点
String lastNode = null;
for (String node : childrenNodes) {
if (znode.endsWith(node)) {
Stat exist = zooKeeper.exists("/" + businessCode + "/" + lastNode,true);
System.out.println(exist == null);
break;
} else {
lastNode = node;
}
}
synchronized (this) {
wait();
}
return true;
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
return false;
}
@Override
public void close() throws Exception {
zooKeeper.delete(znode,-1);
zooKeeper.close();
log.info("我已经释放了锁");
}
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getType() == Event.EventType.NodeDeleted) {
synchronized (this) {
notify();
}
}
}
}
但是我发现了一个问题,那边老师回答含糊不清
首先进行打断点调试,在获取序号最小的子节点这里打上断点
此时进来两个线程exex-1 exec-2
由于是两个线程,那么肯定会在zk上创建两个数据节点000 和 0001
我们看这两个线程获取的子节点信息
exec-1,此时它只查询到了一个节点000,原因是它先到断点这里
再看exec-2,它查询到两个节点,原因就是它后到,它自己有添加了一个临时节点,所以查出来两个也就是线程1的000和自己创建的001
好了,老师这里是关键,我让exec-1这个线程先执行,由于该线程查询来的节点只有一个且与自己的序列号相等,那么它直接获取到锁然后return true,跳出try的时候关闭连接然后删除000节点,000节点被删除了哈!!!!!!!!!!!
如果你不相信被删除,我们看zk,只有001了
紧接着放开exec-2线程,由于它里面有两个child节点 000(已经被exec-1删除了) 以及0001
按照逻辑找到000,然后给它设置监听器,删除了的节点设置监听器是无效的,此时返回的是null !!!,既然都无效了(节点早就被exec-1删除了)谁去触发这个事件?很明显这个监听器没设置成功嘛,就算设置成功也没有线程再去删除000,它早就被删除了嘛
接下来按照逻辑进入wait
这个请求将永远陷入wait
1回答
-
风间影月
2021-03-03
看你的描述貌似没毛病。这个程序也不一定,你在线程的地方wait看看吧,问下那个老师。我觉得应该问题不大的。
012021-03-03
相似问题