利用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.测试结果