Java架构笔记——分布式锁

作者 : 开心源码 本文共2502个字,预计阅读时间需要7分钟 发布时间: 2022-05-12 共203人阅读

写在前面

喜欢的朋友可以关注下专栏:Java架构技术进阶。里面有大量batj面试题集锦,还有各种技术分享,如有好文章也欢迎投稿哦。

分布式锁

并发编程中的锁并发编程的锁机制:synchronized和lock。在单进程的系统中,当存在多个线程可以同时改变某个变量时,就需要对变量或者代码块做同步,使其在修改这种变量时能够线性执行消除并发修改变量。
而同步的本质是通过锁来实现的。为了实现多个线程在一个时刻同一个代码块只能有一个线程可执行,那么需要在某个地方做个标记,这个标记必需每个线程都能看到,当标记不存在时可以设置该标记,其他后续线程发现已经有标记了则等待拥有标记的线程结束同步代码块取消标记后再去尝试设置标记。

分布式环境下,数据一致性问题一直是一个比较重要的话题,而又不同于单进程的情况。分布式与单机情况下最大的不同在于其不是多线程而是多进程。多线程因为可以共享堆内存,因而可以简单的采取内存作为标记存储位置。而进程之间甚至可能都不在同一台物理机上, 因而需要将标记存储在一个所有进程都能看到的地方。 常见的是秒杀场景,订单服务部署了多个实例,如

秒杀商品有4个,第一个客户购买3个,第二个客户购买2个,理想状态下第一个客户能购买成功,第二
个客户提醒购买失败,反之亦然。而实际可能出现的情况是,两个客户都得到库存为4,第一个客户
买到了3个,升级库存之前,第二个客户下了2个商品的订单,升级库存为2,导致出错。

在上面的场景中,商品的库存是共享变量,面对高并发情形,需要保证对资源的访问互斥。在单机环境中,java中其实提供了很多并发解决相关的API,但是这些API在分布式场景中就无能为力了。也就是说单纯的java API 并不能提供分布式锁的能力。分布式系统中,因为分布式系统的分布性,即多线程和多进程并且分布在不同机器中,synchronized和lock这两种锁将失去原有锁的效果,需要我们自已实现分布式锁。

常见的分布式锁如下:

  • 基于数据库实现分布式锁:有性能问题
  • 基于缓存实现分布式锁,如redis
  • 基于zookeeper实现分布式锁

使用setnx实现分布式锁

setnx key value
setnx是将key的值设为value,当且仅当key不存在。若给定的key已经存在,则setnx不做任何动作。
返回1,说明该进程取得锁,setnx将键(lock.id)的值设置为锁的超时时间,当前时间+加上锁的有效时间。
返回0,说明其余进程已经取得了锁,进程不能进入临界区。进程可以在一个循环中不断地尝试setnx操作,以取得锁。

存在死锁的问题

在线程释放锁,即执行del lock.id操作前,需要先判断锁能否已超时。假如锁已超时,那么锁可能已由其余线程取得,这时直接执行del lock.id操作会导致把其余线程已取得的锁释放掉。

获取分布式锁

public boolean lock( long timeout, TimeUnit timeUnit ) throws InterruptedException{    timeout = timeUnit.toMillis( timeout );    long time = timeout + System.currentTimeMillis();    lock.tryLock( timeout, timeUnit );    try{        while ( true )        {            boolean hasLock = tryLock();            if ( hasLock )            {                return(true); /* 取得锁 */            }else if ( timeout < System.currentTimeMillis() )            {                break;            }            Thread.sleep( 1000 );        }    } finally {        if ( lock.isHeldByCurrentThread() )        {            lock.unlock();        }    }    return(false);}public boolean tryLock(){    long    time    = System.currentTimeMillis();    long    timeout = 2000;    String  expires = String.valueOf( timeout + time );    if ( redisService.setnx( "lock.id", expires ) > 0 )    {/* 获取锁,设置超时时间 */        setLockStatus( expires );        return(true);    }else{        String locktime = redisService.get( "lock.id" );/* 检查锁能否超时 */        if ( locktime != null && Long.parseLong( locktime ) < time )        {            String oldlocktime = redis.getset( "lock.id", expires );/* 旧值与当前时间比较 */            if ( oldlocktime != null && locktime.equals( oldlocktime ) )            {/* 获取锁,设置超时时间 */                setLockStatus( expires );                return(true);            }        }        return(false);    }}

释放锁

public boolean unlock(){    if ( lockHolder == Thread.currentThread() )    {/* 判断锁能否超时,没有超时才将互斥量删除 */        if ( lockExpiresTime > System.currentTimeMillis() )        {            redisService.del( "lock.id" );        }        lockHolder = null;        return(true);    }else{        throws new IllegalMonitorStateException( "无法执行解锁操作" );    }}

喜欢的朋友可以关注下专栏:Java架构技术进阶
假如你是Java程序员,对技术提升很感兴趣,欢迎1~5年的工程师可以加入我的Java进阶之路来交流学习:878249276。里面都是同行,有资源共享,还有大量面试题以及解析。欢迎一到五年的工程师加入,正当利用自己每一分每一秒的时间来学习提升自己,不要再用”没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Java架构笔记——分布式锁

发表回复