利用Zookeeper临时节点(客户端异常断开连接后临时节点自动移除)或者Redis SETNX(set if not exists)(设置ttl)可以实现分布式锁,这里先利用zk实现一个
1.启动zk
2.代码实现
2.1 Maven引入zk & zk client
2.2 代码和注释
import org.I0Itec.zkclient.ZkClient;
import java.util.concurrent.CountDownLatch;
public class ZKDistributeLockTest {
public static void main(String[] args) {
// 使用CountDownLunch控制线程同时执行
CountDownLatch countDownLatch = new CountDownLatch(1);
// 开启3个线程模拟分布式环境,分布式环境下每个进程都是一个单独的zkClient
Thread t1 = new Thread(new TestThread(countDownLatch));
Thread t2 = new Thread(new TestThread(countDownLatch));
Thread t3 = new Thread(new TestThread(countDownLatch));
t1.start();
t2.start();
t3.start();
System.out.println("休眠1秒后执行..." + System.currentTimeMillis());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 倒计时结束
countDownLatch.countDown();
}
}
// 线程,尝试在zk上创建临时节点,创建成功则获得锁(执行权)
class TestThread implements Runnable {
// 共享变量
private static Integer CNT = 0;
private ZkClient zkClient;
private CountDownLatch countDownLatch;
public TestThread(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
// 连接zk
private void connect() {
String threadName = Thread.currentThread().getName();
try {
System.out.println(threadName + " 等待执行...");
// 等待倒计时结束
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(threadName + " 请求连接zk..." + System.currentTimeMillis());
zkClient = new ZkClient("192.168.1.217:2181", 20000);
System.out.println(threadName + " 连接成功...");
// 输出目录信息测试
// List<String> children = zkClient.getChildren("/");
// children.forEach(System.out::println);
}
@Override
public void run() {
// 初始化连接(在各个线程里开启连接,模拟分布式环境)
connect();
String threadName = Thread.currentThread().getName();
// 竞争锁
while (true) {
try {
System.out.println(threadName + " 开始竞争锁...");
// 创建zk临时节点
zkClient.createEphemeral("/dl", "test");
System.out.println(threadName + " 获得锁!!!");
// 获得锁后修改共享变量
CNT ++;
System.out.println(threadName + " 释放了锁..." + CNT);
zkClient.delete("/dl");
Thread.sleep(2000);
} catch (Exception e) {
// 创建临时节点失败,表示未获得锁
System.out.println(threadName + " 未获得锁,将重试!!!");
// System.out.println(e.getMessage());
try {
Thread.sleep(1500);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
}
}
3.测试结果
4.Zookeeper如何保证分布式环境下的线程安全(不重复创建节点)?
查看zookeeper源码发现,创建节点是同步方法
而写请求(创建节点)都是在master节点执行,而创建节点又做了同步控制,所以即使是分布式环境创建节点也是线程安全的。