分布式锁

 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

原文地址:https://www.cnblogs.com/chinano1/p/10016983.html