分布式锁的产生原因,单服务集群或者分布式开发的环境下,多个JVM无法进行锁操作。实现方式又Redis 或者zookeeper。
实现原理:在zookeeper上创建临时节点,因为只有一个系统可以创建成功,创建不成功的话会报不存在的错误。当一个拿到锁得系统执行完之后,.close()方法。即关闭临时节点。其它线程可以创建即拿到锁。这个过程利用事件通知zookeeper的handleDataDeleted当节点删除时触发事件,可以用于通知其它线程拿锁。
Redis和zk实现分布式锁的区别:Redis是nosql数据库本质上和数据库解决分布式相仿,本质通过创建变量方式,setNx。而zk是微服务协调工具,他是通过创建临时节点方式实现分布式锁控制。
Redis实现分布式锁,效率更高,稳定性查,zk是稳定性高,但是效率较Redis低。
zk如果遇到非法停止比如宕机的情况下,会出现一段事件死锁,因为临时节点销毁有反应时间。
具体实现代码:
首先创建一个 分布式锁接口
public interface FBSLock {
// 获取锁
public void getLock();
// 释放锁
public void unLock();
}
设计一个抽象模板ZookeeperAbstractLock
public abstract class ZookeeperAbstractLock implements FBSLock {
// 集群连接地址
protected String CONNECTION = "ip";
// zk客户端连接
protected ZkClient zkClient = new ZkClient(CONNECTION);
// path路径
protected String lockPath = "/zkLockpath";
protected CountDownLatch countDownLatch = new CountDownLatch(1);
public void getLock() {
//1.连接ZKClient,
if (tryLock()) {
System.out.println("####Zk获取锁成功######");
} else {//获取失败
waitLock();//等待
getLock();//继续获取递归
}
}
// 获取锁
abstract boolean tryLock();
// 等待锁 这里具体看业务逻辑 可以不等待,就相当于拿不到锁可以干点其它工作,能干点是点
abstract void waitLock();
public void unLock() {
if (zkClient != null) {
System.out.println("#######ZK释放锁#########");
zkClient.close();
}
}
}
具体模板的实现方法:
public class ZookeeperDistrbuteLock extends ZookeeperAbstractLock {
@Override
boolean tryLock() {
try {
zkClient.createEphemeral(lockPath);
return true;
} catch (Exception e) {//回报节点存在错误
return false;
}
}
@Override
void waitLock() {
// 使用zk临时事件监听
IZkDataListener iZkDataListener = new IZkDataListener() {
public void handleDataDeleted(String path) throws Exception {//节点被删除
if (countDownLatch != null) {
countDownLatch.countDown();//因为默认1.当处于0时位于await()的线程可以执行了
}
}
public void handleDataChange(String arg0, Object arg1) throws Exception {//节点被修改这里来用不到
}
};
// 注册事件通知
zkClient.subscribeDataChanges(lockPath, iZkDataListener);
if (zkClient.exists(lockPath)) {
countDownLatch = new CountDownLatch(1);
try {
countDownLatch.await();
} catch (Exception e) {
// TODO: handle exception
}
}
// 监听完毕后,移除事件通知,为了不影响程序效率
zkClient.unsubscribeDataChanges(lockPath, iZkDataListener);
}
}
具体的测试类:
public class OrderService implements Runnable {
private OrderNumGenerator orderNumGenerator = new OrderNumGenerator();
private FBSLock extLock = new ZookeeperDistrbuteLock();
public void run() {
getNumber();
}
public void getNumber() {
try {
extLock.getLock();
String number = orderNumGenerator.getNumber();
System.out.println("线程:" + Thread.currentThread().getName() + ",生成订单id:" + number);
} catch (Exception e) {
} finally {
extLock.unLock();
}
}
public static void main(String[] args) {
System.out.println("多线程生成number");
for (int i = 0; i < 100; i++) {
new Thread(new OrderService()).start();
}
}
}