1、安装zookeeper
参考:https://blog.csdn.net/qq_33612228/article/details/106424541
2、实现思路
1、创建有序临时节点
2、触发“尝试取锁逻辑”,如果自己是临时锁节点序列的第一个,则取得锁,获取锁成功。
3、如果自己不是序列中第一个,则监听前一个锁节点变更。同时阻塞线程。
4、当前一个锁节点变更时,通过watcher恢复线程,然后再次到步骤2“尝试取锁逻辑”
3、springboot集成实现
3.1、pom引入
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
</dependency>
3.2、代码实例
分布式锁工具类
public class ZookeeperLock{
//zk的连接串
String IP = "127.0.0.1:2181";
//计数器对象
CountDownLatch countDownLatch = new CountDownLatch(1);
//zk配置信息
private ZooKeeper zooKeeper;
private static final String LOCK_ROOT_PATH = "/Locks";
private static final String LOCK_NODE_PATH = "Lock_";
private String lockPath;
//打开zookeeper的链接
public ZookeeperLock() throws IOException {
try{
zooKeeper= new ZooKeeper(IP, 10000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if(event.getType()==Event.EventType.None){
if(event.getState()==Event.KeeperState.SyncConnected){
System.out.println("连接创建成功~~~");
countDownLatch.countDown();
}
}
}
});
countDownLatch.await();
}catch(Exception e){
e.printStackTrace();
}
}
//监视器对象,监视上一个节点是否被删除\
private Watcher watcher = new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println(event.getPath() + " 前锁释放");
if(event.getType()== Event.EventType.NodeDeleted){
synchronized (this) {
notifyAll();
}
}
}
};
/**
* 加锁
* @throws Exception
*/
public void acquireLock() throws Exception{
//创建锁节点
createLock();
//尝试获取锁
attempLock();
}
/**
* 创建锁节点
* @throws Exception
*/
public void createLock() throws Exception{
//1、打开zookeeper连接
//2、创建Locks根节点(创建前先用exit判断是否存在,不存在创建)
Stat stat = zooKeeper.exists(LOCK_ROOT_PATH,false);
if(stat == null){
zooKeeper.create(LOCK_ROOT_PATH,new byte[0],ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
}
// 3、创建临时有序节点
lockPath = zooKeeper.create(LOCK_ROOT_PATH + "/" +LOCK_NODE_PATH,new byte[0] ,
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println("创建节点成功~~~" + lockPath);
//进行测试一下~~,看看我们的代码是否有问题~~
}
private void attempLock() throws Exception{
//1、获取locks下的所有子节点
List<String> list = zooKeeper.getChildren(LOCK_ROOT_PATH,false);
//2、对子节点进行排序
Collections.sort(list);
//3、截取掉前面部分
int index = list.indexOf(lockPath.substring(LOCK_ROOT_PATH.length()+1));
//4、对索引进行判断
if(index == 0) {
System.out.println("说明是第1位,获取锁成功!");
return;
}else {
//获取上一个节点的路径
String path = list.get(index-1);
//监视上一个节点
Stat stat = zooKeeper.exists(LOCK_ROOT_PATH+"/"+path,watcher); //监视器对象,监视上一个节点是否被删除
if(stat==null){
//回调,尝试重新获得锁
attempLock();
}else{
//阻塞等待
synchronized(watcher){
watcher.wait();
}
//回调,尝试重新获得锁
attempLock();
}
}
}
/**
* 释放锁
* @throws KeeperException
* @throws InterruptedException
*/
public void releaseLock() throws KeeperException, InterruptedException {
zooKeeper.delete(lockPath, -1);
zooKeeper.close();
System.out.println(" 锁释放:" + lockPath);
}
}
测试调用
public class TicketSeller {
private void sell(){
System.out.println("售票开始");
// 线程随机休眠数毫秒,模拟现实中的费时操作
int sleepMillis = (int) (Math.random() * 2000);
try {
//代表复杂逻辑执行了一段时间
Thread.sleep(sleepMillis);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("售票结束");
}
public void sellTicketWithLock() throws Exception {
ZookeeperLock lock = new ZookeeperLock();
lock.acquireLock();
sell();
lock.releaseLock();
}
public static void main(String[] args) throws Exception {
TicketSeller ticketSeller = new TicketSeller();
for(int i=0;i<1000;i++){
ticketSeller.sellTicketWithLock();
}
}
}
启动程序测试结果:
main 锁获得, lockPath: /Locks/Lock_0000000472
售票开始
售票结束
锁释放:/Locks/Lock_0000000472
main 锁创建: /Locks/Lock_0000000474
等待前锁释放,prelocakPath:Lock_0000000473
/Locks/Lock_0000000473 前锁释放
main 锁获得, lockPath: /Locks/Lock_0000000474
售票开始
售票结束
锁释放:/Locks/Lock_0000000474
参考文章:https://blog.csdn.net/liyiming2017/article/details/83786331