分布式锁的要求:
a:互斥
b:宕机避免死锁
c:只能自己解锁
d 能具备公平性最好
1 数据库版本
https://blog.csdn.net/linsongbin1/article/details/79444274
2 redis
3zookeeper
a:pom文件
<!-- 原生zk支持--> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.3.6</version> </dependency> <!-- zkclient原生客户端支持--> <dependency> <groupId>com.github.sgroschupf</groupId> <artifactId>zkclient</artifactId> <version>0.1</version> </dependency>
b:必要参数采用yml配置注入
zklock:
url: 127.0.0.1:2181
rootPath: /newlock17
c: 创建锁工具类
要点介绍:
1:实现lock接口,开闭原则
2:构造函数中实例化ZkClient
3: 在根节点下创建临时顺序节点记得加分隔符“/”:
package com.test.domi.common.utils.lock; import org.I0Itec.zkclient.IZkDataListener; import org.I0Itec.zkclient.ZkClient; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; public class ZKlock implements Lock { private ZkClient zkClient; private String rootPath; private String currentPath; private String beforePath; public ZKlock(String url,String rootPath){ this.rootPath = rootPath; zkClient = new ZkClient(url); if (!zkClient.exists(rootPath)) { try { zkClient.createPersistent(rootPath); } catch (RuntimeException e) { } } } @Override public boolean tryLock() { if (currentPath == null) { currentPath = zkClient.createEphemeralSequential(rootPath + "/","aaa"); } List<String> childrens = zkClient.getChildren(rootPath); Collections.sort(childrens); if (currentPath.equals(rootPath + "/" + childrens.get(0))) { return true; }else{ int curIndex = childrens.indexOf(currentPath.substring(rootPath.length() + 1)); beforePath = rootPath + "/" + childrens.get(curIndex - 1); } return false; } @Override public void lock() { if (!tryLock()) { waiForLock(); lock(); } } @Override public void unlock() { zkClient.delete(currentPath); } private void waiForLock(){ CountDownLatch cdl = new CountDownLatch(1); IZkDataListener listener = new IZkDataListener() { @Override public void handleDataChange(String s, Object o) throws Exception { } @Override public void handleDataDeleted(String s) throws Exception { cdl.countDown(); } }; zkClient.subscribeDataChanges(beforePath,listener); if (zkClient.exists(beforePath)) { try { cdl.await(); } catch (InterruptedException e) { } } zkClient.unsubscribeDataChanges(beforePath,listener); } @Override public void lockInterruptibly() throws InterruptedException { } @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { return false; } @Override public Condition newCondition() { return null; } }
currentPath = zkClient.createEphemeralSequential(rootPath + "/","aaa");
d: 将锁工具类交给Spring管理,这样就不需要每次new的时候传入客户端的连接ip,一次注入终生受用,
但是切记@Scope为多例,因为ZKLock里面的全局变量不能再多线程共享。
package com.test.domi.config; import com.test.domi.common.utils.lock.ZKlock; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; @Configuration public class ZkLockConfig { @Value("${zklock.rootPath}") private String rootPath; @Value("${zklock.url}") private String url; @Bean @Scope("prototype") public ZKlock getZKlock(){ return new ZKlock(url,rootPath); } }
e : 使用方式:
要点:
1- zklock.unlock(); 一般要放在finally中
2- 重入之后一般需要判断业务是否执行过,已经执行过的应立即退出
package com.test.domi.controller;
import com.test.domi.common.utils.SpringContextUtil;
import com.test.domi.common.utils.lock.ZKlock;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/zk")
public class ZKController {
private int k = 1;
@GetMapping("/lock")
public Boolean getLock() throws Exception{
for (int i = 0; i < 50; i++) {
new Thread(new Runnable() {
@Override
public void run() {
ZKlock zklock = SpringContextUtil.getBean(ZKlock.class);
zklock.lock();
try {
System.out.println(Thread.currentThread().getName() + "获得锁,生成唯一的单据编号" + k++);
} catch (Exception e) {
e.printStackTrace();
}finally {
zklock.unlock();
}
}
}).start();
}
return true;
}
}