分布式锁主要解决多个进程访问同一资源时的资源竞争问题。主要有三种解决方案,基于数据库实现分布式锁,基于缓存实现分布式锁和基于zookeeper实现分布式锁。
一、三种实现方案介绍
1、基于数据库实现分布式锁
基于数据库实现分布式锁实现起来比较简单,但是其有如下缺点:
(1)性能较差,容易出现单点故障,此由数据库连接池的连接数决定的;
(2)锁没有失效时间,容易死锁,经常出现的情况是线程死了,数据库记录没有删除,导致出现死锁;
(3)非阻塞式。
2、基于缓存实现分布式锁
基于缓存实现分布式锁原理和基于数据库类似,其性能和并发量相较于数据库实现要大大提升,但是也会有如下缺点:
(1)需要为锁设置失效时间,但是失效时间无法准确设定。
3、基于zookeeper实现分布式锁
基于zookeeper相较于前面两种方案,其性能和可靠性都有所提升,也是最长采用的方案,其特点如下:
(1)实现简单;
(2)可靠性好;
(3)性能好。
二、zookeeper简介
zookeeper是一个分布式的、开放源代码的分布式应用程序协调服务,是Hadoop和Hbase的重要组件。在zk中zNode是一个跟Unix或者Windows文件系统路径相似的节点,可以往这个节点存储或者获取数据。
通过客户端可以对zNode进行增删改查操作,还可以注册watcher监控zNode的变化。zNode跟文件系统一样,名称不可重复。
zk的节点类型包括:持久节点,持久顺序节点,临时节点,临时顺序节点。临时节点跟会话有关,连接断掉,节点会被自动删除。
三、基于zookeeper实现分布式锁的代码示例
1、定义Lock接口
public interface Lock {
public void lock();
public void unLock();
}
2、Lock抽象类(模板模式)
public abstract class AbstractLock implements Lock{
protected static String host = "127.0.0.1";
protected static int port = 2181;
protected static String path = "/lock";
protected static ZkClient zkClient = new ZkClient(host, port);
@Override
public void lock() {
if(tryLock()){
System.out.println("获取锁成功!!!");
}else{
//如果尝试获取锁失败,则等待获取锁
waitForLock();
lock();
}
}
protected abstract boolean tryLock();
protected abstract void waitForLock();
}
3、锁的具体实现类
public class ZookeeperLock extends AbstractLock{
private CountDownLatch cdl =null;
/**
* 解锁即删除临时节点
*/
@Override
public void unLock() {
zkClient.delete(path);
}
/**
* 如果创建临时节点成表示获取锁成功
*/
@Override
protected boolean tryLock() {
try{
zkClient.createEphemeral(path);
return true;
}catch(Exception e){
return false ;
}
}
@Override
protected void waitForLock() {
IZkDataListener listener = new IZkDataListener(){
@Override
public void handleDataChange(String dataPath, Object data) throws Exception {
}
@Override
public void handleDataDeleted(String dataPath) throws Exception {
//监听临时节点删除事件
if(cdl!=null){
cdl.countDown();
}
}
};
//订阅节点改变事件
zkClient.subscribeDataChanges(path, listener);
if(zkClient.exists(path)){
//线程在此阻塞,直到临时节点删除事件触发
cdl = new CountDownLatch(1);
try {
cdl.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
zkClient.unsubscribeDataChanges(path, listener);
}
}
网友评论