利用zookeeper的临时节点实现分布式锁,这种方法简单,断开连接后能自动删除临时节点,相当于已获得锁的调用者挂掉后自动释放锁,但当调用者太多,会出现“惊群”现象。
/**
* zookeeper锁实现
* @author skymr
*
*/
public class ZookeeperLock implements Lock, Watcher{
public ZookeeperLock(String url, int sessionTimeOut, String path){
this.path = path;
try {
//url是zookepper服务器的地址
zk = new ZooKeeper(url, sessionTimeOut, this);
latch.await();
} catch (Exception e) {
e.printStackTrace();
}
}
//zk客户端
private ZooKeeper zk;
//结点路径
private String path;
//用于初始化zk的,zk连接是异步的,但连接成功后才能进行调用
private CountDownLatch latch = new CountDownLatch(1);
public void lock() {
if(!tryLock()){
try {
Stat s = zk.exists(path, false);
if(s == null){
//创建节点失败,但节点现在这不存在了
lock();
return;
}
} catch (Exception e1) {
}
//如果尝试加锁失败,则进入等待
synchronized(zk){
System.out.println(Thread.currentThread().getName() +" lock失败,进入等待");
try {
zk.wait();
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName() +" lock等待完成");
}
//等待别人释放锁后,自己再去加锁
lock();
}
else{
System.out.println(Thread.currentThread().getName() +" lock成功");
}
}
public void lockInterruptibly() throws InterruptedException {
}
public boolean tryLock() {
try {
//加锁代码是创建一个节点
zk.create(path, "111".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
//不抛异常就表示创建成功啦
return true;
} catch (Exception e) {
try {
//创建失败,那就监听此节点的动态, 当此节点删除后要重新加锁的
zk.getChildren(path, this);
//这里不应该用zk.exists(path, true)吗,不晓得为什么getChildren也可以
} catch (Exception e1) {}
}
finally{
}
return false;
}
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
public void unlock() {
try {
//释放锁,删除节点
zk.delete(path, -1);
} catch (Exception e) {
}
}
public Condition newCondition() {
return null;
}
public void process(WatchedEvent event) {
System.out.println(event);
if(event.getType() == EventType.None){
latch.countDown();
}
if(event.getType() == EventType.NodeDeleted && event.getPath().equals(path)){
synchronized(zk){
zk.notifyAll();
}
System.out.println(Thread.currentThread().getName() +" 通知Lock等待中的线程重试加锁");
}
}
}
说明:
只实现了Lock接口的 trylock, lock,与unlock方法
测试:
创建maven项目
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>club.skymr</groupId>
<artifactId>zklock</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>zklock</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>
</dependencies>
</project>
测试类:
public class LockTest {
// private static Lock lock = new ReentrantLock();
// private static Lock lock = new ZookeeperLock("localhost", 3000, "/node");
public static void main(String[] args) throws Exception{
for(int i = 0; i < 2; i++){
new Thread(){
public void run(){
Lock lock = new ZookeeperLock("localhost", 3000, "/node");
try{
lock.lock();
System.out.println(Thread.currentThread().getName() + "开始执行");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + "执行完成 ");
}
finally{
lock.unlock();
}
}
}.start();
}
}
}
测试结果:
WatchedEvent state:SyncConnected type:None path:null
WatchedEvent state:SyncConnected type:None path:null
Thread-0 lock成功
Thread-0开始执行
Thread-1 lock失败,进入等待
Thread-0执行完成
WatchedEvent state:SyncConnected type:NodeDeleted path:/node
Thread-1 lock等待完成
Thread-1-EventThread 通知Lock等待中的线程重试加锁
Thread-1 lock成功
Thread-1开始执行
Thread-1执行完成
代码比较粗糙,但已实现基本的分布式锁