分布式锁
1 public class ZKLockDemo { 2 public static void main(String[] args) throws Exception { 3 4 String zkConnection = "192.168.40.136:2181,192.168.40.137:2181,192.168.40.138:2181"; 5 6 //重试策略 maxRetries:最大重试次数 baseSleepTimeMs:初始sleep时间 7 RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); 8 //采用Fluent风格初始会话 9 CuratorFramework client = CuratorFrameworkFactory.builder().connectString(zkConnection).sessionTimeoutMs(5000).retryPolicy(retryPolicy).build(); 10 //启动会话 11 client.start(); 12 13 //创建分布式锁, 锁空间的根节点路径为/lock 14 InterProcessMutex mutex = new InterProcessMutex(client, "/lock"); 15 //获取锁 16 mutex.acquire(); 17 18 //获得了锁, 进行业务流程 19 System.out.println("Enter mutex"); 20 21 //完成业务流程, 释放锁 22 mutex.release(); 23 //关闭客户端 24 client.close(); 25 26 } 27 }
获取锁代码:
1 @Override 2 public void acquire() throws Exception 3 { 4 if ( !internalLock(-1, null) ) 5 { 6 throw new IOException("Lost connection while trying to acquire lock: " + basePath); 7 } 8 }
1 @Override 2 public boolean acquire(long time, TimeUnit unit) throws Exception 3 { 4 return internalLock(time, unit); 5 }
internalLock(time, unit)中,time=-1表示锁被占用时,永久阻塞
1 private boolean internalLock(long time, TimeUnit unit) throws Exception 2 { 3 /* 4 Note on concurrency: a given lockData instance 5 can be only acted on by a single thread so locking isn't necessary 6 */ 7 8 Thread currentThread = Thread.currentThread(); 9 10 LockData lockData = threadData.get(currentThread); 11 if ( lockData != null ) 12 { 13 // 可重入锁,计数器加一,在释放时需要释放多次 14 lockData.lockCount.incrementAndGet(); 15 return true; 16 } 17 //获取锁 18 String lockPath = internals.attemptLock(time, unit, getLockNodeBytes()); 19 if ( lockPath != null ) 20 { 21 LockData newLockData = new LockData(currentThread, lockPath); 22 threadData.put(currentThread, newLockData); 23 return true; 24 } 25 26 return false; 27 }
1 String attemptLock(long time, TimeUnit unit, byte[] lockNodeBytes) throws Exception 2 { 3 final long startMillis = System.currentTimeMillis(); 4 final Long millisToWait = (unit != null) ? unit.toMillis(time) : null; 5 final byte[] localLockNodeBytes = (revocable.get() != null) ? new byte[0] : lockNodeBytes; 6 int retryCount = 0; 7 8 String ourPath = null; 9 boolean hasTheLock = false; 10 boolean isDone = false; 11 //自选获取锁 12 while ( !isDone ) 13 { 14 isDone = true; 15 16 try 17 { 18 //创建临时有序子节点 19 ourPath = driver.createsTheLock(client, path, localLockNodeBytes); 20 ////判断是否获得锁(子节点序号最小),获得锁则直接返回,否则阻塞等待前一个子节点删除的通知 21 hasTheLock = internalLockLoop(startMillis, millisToWait, ourPath); 22 } 23 catch ( KeeperException.NoNodeException e ) 24 { 25 // gets thrown by StandardLockInternalsDriver when it can't find the lock node 26 // this can happen when the session expires, etc. So, if the retry allows, just try it all again 27 if ( client.getZookeeperClient().getRetryPolicy().allowRetry(retryCount++,
System.currentTimeMillis() - startMillis, RetryLoop.getDefaultRetrySleeper()) ) 28 { 29 isDone = false; 30 } 31 else 32 { 33 throw e; 34 } 35 } 36 } 37 38 if ( hasTheLock ) 39 { 40 return ourPath; 41 } 42 43 return null; 44 }
优点:
1. 可靠性高、实现简单
2. zookeeper因为临时节点的特性,如果因为其他客户端因为异常和zookeeper连接中断了,那么节点会被删除,意味着锁会被自动释放
3. zookeeper本身提供了一套很好的集群方案,比较稳定
4. 释放锁操作,会有watch通知机制,也就是服务器端会主动发送消息给客户端这个锁已经被释放了
数据库实现分布式锁:
建表语句:
CREATE TABLE `lock` ( `id` INT NOT NULL AUTO_INCREMENT, `method_name` VARCHAR(200) NOT NULL DEFAULT '0', `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE INDEX `method_name` (`method_name`) ) COMMENT='分布式锁' COLLATE='utf8mb4_0900_ai_ci' ;
获取锁伪代码:
try{ insert into lock(method_name) values(‘methodName’); return true; }Catch(DuplicateException e){ return false; }
释放锁:
delete from lock where method_name="methodName";
缺点:
1. 锁没有失效时间,一旦解锁操作失败,就会导致锁记录一直在数据库中,其他线程无法再获得到锁
2. 锁是非阻塞的,数据的insert操作,一旦插入失败就会直接报错。没有获得锁的线程并不会进入排队队列,要想再次获得锁就要再次触发获得锁操作
3. 锁是非重入的,同一个线程在没有释放锁之前无法再次获得该
基于缓存的分布式锁实现
以后补充
参考:
https://blog.csdn.net/qiangcuo6087/article/details/79067136