zoukankan      html  css  js  c++  java
  • redis 实现分布式锁 和基本锁

    1 为什么要使用锁

    运行以下代码

    # 模拟多线程
    
    import threading
    
    def change_it(n):
        global num
        for i in range(1000000):
            num = num + n
            num = num - n
    
        print(num,'555')
    threass=[
        threading.Thread(target=change_it,args=(8,)),  #
        threading.Thread(target=change_it,args=(10,)),
    ]
    
    [ t.start() for t in threass   ]
    
    [ t.join() for t in threass   ]
    
    print(num,'9999999999999')  

    总结 运行结果每次都不同

    加锁后运行

    # 模拟多线程
    
    import threading
    
    num=0
    lock=threading.Lock()
    def change_it(n):
        global num
        if lock.acquire():
            try:
                for i in range(1000000):
                    num = num + n
                    num = num - n
            finally:
                lock.release()
                print('释放锁')
        print(num)
    
    
    threass=[
        threading.Thread(target=change_it,args=(8,)),  #
        threading.Thread(target=change_it,args=(10,)),
    ]
    
    [ t.start() for t in threass   ]
    
    [ t.join() for t in threass   ]
    
    print(num,'9999999999999')
    

      

    在多线程并发的情况下,我们可以使用锁来保证一个代码块在同一时间内只能由一个线程访问。比如Java的synchronized关键字和Reentrantlock类等等。

    这样子可以保证在同一个JVM进程内的多个线程同步执行。

    如果在分布式的集群环境中,如何保证不同节点的线程同步执行呢?

    怎么才能在分布式系统中,实现不同线程对代码和资源的同步访问?

    对于单进程的并发场景,我们可以使用语言和类库提供的锁。对于分布式场景,我们可以使用分布式锁。

    那么怎么才能实现分布式系统中的锁呢?

    分布式锁有许多中实现方法,下面简单列举一下。

    分布式锁的实现有哪些?

    1.Memcached分布式锁

    利用Memcached的add命令。此命令是原子性操作,只有在key不存在的情况下,才能add成功,也就意味着线程得到了锁。

    2.Redis分布式锁

    和Memcached的方式类似,利用Redis的setnx命令。此命令同样是原子性操作,只有在key不存在的情况下,才能set成功。(setnx命令并不完善,后续会介绍替代方案)

    3.Zookeeper分布式锁

    利用Zookeeper的顺序临时节点,来实现分布式锁和等待队列。Zookeeper设计的初衷,就是为了实现分布式锁服务的。

    首先讲一下Redis的分布式锁,这种实现方式比较有代表性。

    如何用Redis实现分布式锁?

    Redis分布式锁的基本流程并不难理解,但要想写得尽善尽美,也并不是那么容易。在这里,我们需要先了解分布式锁实现的三个核心要素:

    1.加锁

    最简单的方法是使用setnx命令。key是锁的唯一标识,按业务来决定命名。比如想要给一种商品的秒杀活动加锁,可以给key命名为 “lock_sale_商品ID” 。而value设置成什么呢?锁的value值为一个随机生成的UUID。我们可以姑且设置成1。加锁的伪代码如下:    

    setnx(key,1)

    当一个线程执行setnx返回1,说明key原本不存在,该线程成功得到了锁;当一个线程执行setnx返回0,说明key已经存在,该线程抢锁失败。

    2.解锁

    有加锁就得有解锁。当得到锁的线程执行完任务,需要释放锁,以便其他线程可以进入。释放锁的最简单方式是执行del指令,伪代码如下:

    del(key)

    释放锁之后,其他线程就可以继续执行setnx命令来获得锁。

    3.锁超时

    锁超时是什么意思呢?如果一个得到锁的线程在执行任务的过程中挂掉,来不及显式地释放锁,这块资源将会永远被锁住,别的线程再也别想进来。

    所以,setnx的key必须设置一个超时时间,单位为second,以保证即使没有被显式释放,这把锁也要在一定时间后自动释放,避免死锁。setnx不支持超时参数,所以需要额外的指令,伪代码如下:

    expire(key, 30)
    import redis
    r=redis.Redis(host='localhost',port=6379)
    a=r.setnx(5,'zsj')   # 存
    # r.expire(5,30)    # 设置30s时间
    

      

  • 相关阅读:
    ESRI Shapefiles (SHP)
    Python与开源GIS:在OGR中使用SQL语句进行查询
    [推荐]网店代销的卖家,你的宝贝名称修改了吗?
    怎么把经纬度转换成标准的度分秒单位
    如何提高淘宝流量
    十八种方法提升淘宝店流量
    mysql备份数据库几种方法
    Linux查看文件编码格式及文件编码转换
    MySQL 修改字段类型或长度
    mysql外键使用和级联
  • 原文地址:https://www.cnblogs.com/zhangshijiezsj/p/14207495.html
Copyright © 2011-2022 走看看