zookeeper分布式锁原理及项目应用
https://www.cnblogs.com/linjiqin/p/6052031.html
pom.xml
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.11</version>
</dependency>
logback.xml
<?xml version="1.0"?>
<configuration>
<!-- ch.qos.logback.core.ConsoleAppender 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder charset="UTF-8">
<pattern>[%-5level] %d{HH:mm:ss.SSS} [%thread] %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 日志级别 -->
<root>
<level value="info" />
<appender-ref ref="console" />
</root>
</configuration>
GenerateOrderNoService
public class GenerateOrderNoService {
static int count = 0;
public static int getOrderNo() {
count++;
return count;
}
}
main
public class OrderService implements Runnable {
ZookeeperLock lock = new ZookeeperLock();
@Override
public void run() {
int orderNo = getOrderNo();
System.out.println(Thread.currentThread().getName() + " 测试 " + " : " + orderNo);
}
private int getOrderNo() {
try {
lock.lock();
int orderNo = 0;
for (int i = 1; i <= 1000; i++) {
orderNo = GenerateOrderNoService.getOrderNo();
}
return orderNo;
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return 0;
}
public static void main(String[] args) throws InterruptedException {
OrderService orderService = new OrderService();
for (int i = 1; i <= 5; i++) {
Thread thread = new Thread(orderService);
thread.start();
// Thread.sleep(200);
}
}
}
ZookeeperLock
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
* 持久化根节点: /lock
* 最小节点才能获取锁
* 非最小节点watch监听上一个节点,监听是否删除操作,删除才获取锁,否则阻塞等待
* 获取锁,执行完毕业务,需要释放锁,释放锁即删除临时节点
*/
public class ZookeeperLock {
private String host = "192.168.239.131:2181,192.168.239.132:2181,192.168.239.133:2181,192.168.239.104:134";
private ZkClient zkClient;
private String persistentNodePath = "/lock";
private ThreadLocal<String> curTl = new ThreadLocal<>(); //当前节点 /lock/t0001
private ThreadLocal<String> beforeTl = new ThreadLocal<>(); //前个节点 /lock/t0002 lastZoneId - zoneId = 1
public ZookeeperLock() {
zkClient = new ZkClient(host);
if (!zkClient.exists(persistentNodePath)) {
zkClient.createPersistent(persistentNodePath,"");
}
}
/**
* 尝试获取锁
*
* 最小节点 : 得到锁。
* 非最小节点 : 等待锁。
*
* @return true:得到锁, false:未得到锁
*/
private boolean tryLock() {
//创建当前临时节点
String currentEphemeralZoneId = curTl.get();
if(currentEphemeralZoneId == null) {
currentEphemeralZoneId = zkClient.createEphemeralSequential(persistentNodePath + "/", null);
System.out.println(Thread.currentThread() + " 创建临时节点 " + currentEphemeralZoneId);
curTl.set(currentEphemeralZoneId);
}
//获取/lock下所有子节点
List<String> children = zkClient.getChildren(persistentNodePath);
//排序
Collections.sort(children);
//获取当前节点
String first = children.stream().findFirst().get();
//是否为最小节点
if (currentEphemeralZoneId.equals(persistentNodePath + "/" + first)) {
return true;
} else {
//获取上一个节点
int curIndex = children.indexOf(currentEphemeralZoneId.substring(persistentNodePath.length() + 1));
String beforeEphemeralZoneId = persistentNodePath + "/" + children.get(curIndex - 1);
beforeTl.set(beforeEphemeralZoneId);
System.out.println(Thread.currentThread() + " 当前:" + currentEphemeralZoneId + ", 前个:" + beforeEphemeralZoneId);
return false;
}
}
/**
* 等待锁 : 未获取到锁 则阻塞并等待锁
* 注意:要先注册监听器,
* 如果后注册监听器,会导致监听器未启动,线程便进入await,await等不到countDownLatch.countDown();就会一直处于等待状态,陷入死锁。
*
* @return
*/
private void waitForLock() throws InterruptedException {
String beforeEphemeralZoneId = beforeTl.get();
String currentEphemeralZoneId = curTl.get();
//线程计数器
CountDownLatch countDownLatch = new CountDownLatch(1);
//构造监听器
IZkDataListener listener = new IZkDataListener() {
@Override
public void handleDataChange(String dataPath, Object data) throws Exception {
System.out.println("节点改变");
}
@Override
public void handleDataDeleted(String dataPath) throws Exception {
System.out.println(Thread.currentThread() + " 监听到" + beforeEphemeralZoneId + "被删除, 唤醒" + currentEphemeralZoneId);
//删除上个节点, 唤醒当前线程
countDownLatch.countDown();
}
};
//开启监听器
zkClient.subscribeDataChanges(beforeEphemeralZoneId, listener);
//二次确认:是否存在前个节点
if(zkClient.exists(beforeEphemeralZoneId)) {
//存在:阻塞当前线程
System.out.println(Thread.currentThread() + " " + currentEphemeralZoneId +" 进等待了! ");
countDownLatch.await();
System.out.println(currentEphemeralZoneId +" 拿到锁了!");
} else {
//不存在:关闭监听器
zkClient.unsubscribeDataChanges(beforeEphemeralZoneId, listener);
}
}
/**
* 获取锁
* @return
*/
public void lock() throws Exception {
if(!tryLock()) {
waitForLock();
lock();
}
}
/**
* 释放锁
* @return
*/
public void unlock() {
String currentEphemeralZoneId = curTl.get();
System.out.println("释放锁,删除节点 " + currentEphemeralZoneId);
zkClient.delete(currentEphemeralZoneId);
curTl.remove();
}
}

浙公网安备 33010602011771号