锁是为了在多线程的场景中保证数据安全而增加的一种手段,Java中常用的有CountdownLatch,ReentrantLock等单应用中的锁,在现在处处都是分布式的场景需求下就不能满足了,所以就出现了分布式锁。
不同的物理节点有各自的线程,但是他们会访问同一个资源,但是不允许同一时刻访问,所以就有了分布式锁
例如
我们可以通过数据库编写sql来实现分布式锁,但是这种在高并发下性能会出问题,
还有常用的redis实现分布式锁,这个是我们用的最多的一种高性能高并发的实现方式。
今天介绍的一种是通过中间件zookeeper实现分布式锁,也是支持高性能高并发的。
想想实现一个锁想到哪些关键点 ?
争抢锁:只有一个人可以获取锁
获得锁的节点挂了,临时节点 会自动释放
获得锁的人,可以主动释放锁
锁被释放,删除 其他人怎么知道
主动轮训,监听心跳:存在延迟,节点多的情况话压力很大。
复制代码
根据zk的节点看看是否满足锁
创建持久化节点
zk是创建节点保存数据的,相同节点只允许创建一次,所以我们可以通过成功创建节点实现获取锁的情况。
关键代码
zk.create("/lock", threadId.getBytes() , ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
复制代码
结果:
如图只有一个线程获取锁,其他线程都出现异常:NodeExists for /lock ,可以保证同一时刻只有一个线程获取锁。然后获得锁的线程逻辑执行结束后应该删除锁。
存在的问题:如果线程崩溃了,锁就无法释放了,最终导致死锁
持久化节点不行,持久化顺序节点自然也不行了
创建临时节点
临时节点:在客户端断开连接的时候就会自动删除
关键代码:
zk.create("/lock", threadId.getBytes() , ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
CreateMode.EPHEMERAL
意思就是创建临时节点 ,也就是当线程崩溃,无法主动释放锁的时候,会自动删除,避免死锁。
但是
还存在的问题:没有获取锁的线程会出现错误,则需要不断重试,通过死循环直到获取锁。