基于zk的分布式锁实现
1、pom依赖的包
<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>com.ganymede</groupId>
<artifactId>zk-distributed-lock</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>zk-distributed-lock</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<zookeeper.version>3.4.5</zookeeper.version>
<curator.version>2.9.0</curator.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>${zookeeper.version}</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${curator.version}</version>
</dependency>
</dependencies>
</project>
2、操作zk的工厂类
package com.ganymede.lock;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
/**
* 操作zk的工厂类
*
* @author Ganymede
*
*/
public class ZookeeperFactory {
private static final String connectString = "spark1:2181,spark2:2181,spark3:2181";
public static CuratorFramework curatorClient = null;
public static CuratorFramework createClient() {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
if (curatorClient != null) {
return curatorClient;
}
curatorClient = CuratorFrameworkFactory.builder().connectString(connectString).retryPolicy(retryPolicy)
.sessionTimeoutMs(1000).connectionTimeoutMs(1000).build();
curatorClient.start();
return curatorClient;
}
}
3、模拟多线程时,出问题的程序代码块
package com.ganymede.lock;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 模拟多线程时,出问题的程序代码块
* @author Ganymede
*
*/
public class LimitedResource {
private final AtomicBoolean ab = new AtomicBoolean(false);
public void useResource() throws InterruptedException {
if (!ab.compareAndSet(false, true)) {
throw new IllegalArgumentException("线程" + Thread.currentThread().getId() + "非法操作!");
}
System.out.println("线程" + Thread.currentThread().getId() + "正在操作");
Thread.sleep(1000);
ab.set(false);
}
}
4、zk分布式锁实现,不可重用锁
package com.ganymede.lock;
import java.util.concurrent.TimeUnit;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex;
/**
* 不可重用锁
* 同一线程,不能两次调用acquire
* 不会同时拥有两把锁
*
* @author Ganymede
*
*/
public class ZkDistributedLock {
private InterProcessSemaphoreMutex lock;
private LimitedResource resource;
private String threadName;
public ZkDistributedLock(CuratorFramework client, String path, LimitedResource resource, String threadName) {
lock = new InterProcessSemaphoreMutex(client, path);
this.resource = resource;
this.threadName = threadName;
}
public void execute(long time, TimeUnit unit) {
// acquire(time, unit),获取到了锁返回true,有超时机制,在unit为单位的time中还没获取到锁,就超时了
try {
while (!lock.acquire(time, unit)) {
System.out.println("线程" + threadName + Thread.currentThread().getId() + "正在着急等待获取锁");
}
System.out.println("线程" + threadName + Thread.currentThread().getId() + "获取锁");
resource.useResource();
System.out.println("线程" + threadName + Thread.currentThread().getId() + "释放锁");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// release()释放锁,获取到锁之后,完成了任务,必须释放
lock.release();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
5、模似多线程并发操作
package com.ganymede.lock;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.curator.framework.CuratorFramework;
/**
* 模似多线程并发操作
*
* @author Ganymede
*
*/
public class ZkDistributedLockTest {
public static void main(String[] args) throws InterruptedException {
final LimitedResource lr = new LimitedResource();
final String path = "/ganymede/lock";
ExecutorService service = Executors.newFixedThreadPool(7);
for (int i = 0; i < 7; i++) {
Callable<Void> task = new Callable<Void>() {
@Override
public Void call() throws Exception {
CuratorFramework client = ZookeeperFactory.createClient();
ZkDistributedLock lock = new ZkDistributedLock(client, path, lr, "thread");
lock.execute(5, TimeUnit.SECONDS);
return null;
}
};
service.submit(task);
}
service.shutdown();
service.awaitTermination(25, TimeUnit.MINUTES);
}
}
6、运行结果
线程thread14获取锁
线程14正在操作
线程thread14释放锁
线程thread15获取锁
线程15正在操作
线程thread15释放锁
线程thread16获取锁
线程16正在操作
线程thread16释放锁
线程thread12获取锁
线程12正在操作
线程thread12释放锁
线程thread11获取锁
线程11正在操作
线程thread17正在着急等待获取锁
线程thread13正在着急等待获取锁
线程thread11释放锁
线程thread17获取锁
线程17正在操作
线程thread17释放锁
线程thread13获取锁
线程13正在操作
线程thread13释放锁
多线程抢占资源下,只有一个线程可以获得一次锁,并且不能两次获取。
7、查看zk下的目录