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(); } }