zoukankan      html  css  js  c++  java
  • 分布式锁

     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

  • 相关阅读:
    微信小程序----map组件实现检索【定位位置】周边的POI
    nginx负载均衡和inotify+rsync文件同步
    mysql主从同步配置和读写分离实现(中间件Amoeba)
    微信小程序----Uncaught ReferenceError: ret is not defined
    微信小程序----wx:key(Now you can provide attr "wx:key" for a "wx:for" to improve performance.)
    回档|NOIP2012 同余方程
    回档|欧几里得算法和扩展欧几里得算法
    回档|Splay tree应用之郁闷的出纳员
    回档|史观小结
    回档|乘积最大
  • 原文地址:https://www.cnblogs.com/chinano1/p/10016983.html
Copyright © 2011-2022 走看看