redis锁

作者: 7d972d5e05e8 | 来源:发表于2020-06-13 10:27 被阅读0次

    1、单节点的redis锁
    优点:快
    缺点:不安全(redis节点断电,网络不通,锁过期等等)

    2、多节点redis锁,即RedLock
    参考文章:https://www.cnblogs.com/baichunyu/p/11631750.html

    优点:快,相对安全(上了分布式redis,解决了单点问题)
    缺点:还是不完全安全。

    为啥说它相对安全呢?
    可以参考这篇文章:https://www.cnblogs.com/baichunyu/p/11631777.html
    英文博客:How to do distributed locking

    总结下:应用client1自己由于gc导致STW的话,有可能因为时间过长,导致redLock的key过期,其他应用client2获取到锁,导致不安全。gc只是其中一个原因,还包括:网络包的延迟,cpu抢占,页中断等等可能造成pause的地方,都是不安全的。

    二、synchronized锁

    我根据图总结了下:
    1、无竞争情况下,是偏向锁。不管是单线程,还是多线程交替执行,只要没有竞争发生。
    2、轻量级锁:只要出现一次竞争,升级为轻量级。并且竞争不激烈(自旋次数内都能拿到锁),就一直在轻量级锁
    3、只要有一个竞争线程在自旋次数达到拿不到锁,它就把锁升级为重量级锁。
    锁只能膨胀,不能缩小。

    原文地址:https://blog.csdn.net/wojiao228925661/article/details/100145157

    啃碎并发(七):深入分析Synchronized原理

    synchronized重量级锁是通过JVM的monitor来实现的,monitor是基于操作系统的mutex来实现。JVM的monitor用来实现可重入,等待队列,阻塞队列等等JVM层面的功能。monitor在JVM是c++实现,结构如下:

    ObjectMonitor() {
        _header       = NULL;
        _count        = 0;
        _waiters      = 0,
        _recursions   = 0;
        _object       = NULL;
        _owner        = NULL;
        _WaitSet      = NULL;
        _WaitSetLock  = 0 ;
        _Responsible  = NULL ;
        _succ         = NULL ;
        _cxq          = NULL ;
        FreeNext      = NULL ;
        _EntryList    = NULL ;
        _SpinFreq     = 0 ;
        _SpinClock    = 0 ;
        OwnerIsThread = 0 ;
    

    很明显里面的_owner用来实现重入功能,_WaitSet用来实现等待队列,_EntryList实现阻塞队列。

    _owner:指向持有ObjectMonitor对象的线程
    _WaitSet:存放处于wait状态的线程队列
    _EntryList:存放处于等待锁block状态的线程队列
    _recursions:锁的重入次数
    _count:用来记录该线程获取锁的次数

    多线程获取锁的时候,会先进入_EntryList排队,然后monitor会选择对头拿锁。然后其他线程继续在_EntryList阻塞着。等到获取线程锁执行wait(),暂时放弃锁的时候,该线程会自动进入_WaitSet等待被唤醒(没唤醒前是不参与竞争锁的),然后唤醒_EntryList其中一个线程。如果这个时候条件到达换行了_WaitSet里面的某个线程,那么它会加入到_EntryList和其他正在竞争锁的线程一起,重新竞争锁。

    image.png

    PS:序号4的线程,在被notify唤醒后,不应该直接进入The Owner。它应该进入阻塞队列,一同参与竞争锁。

    四、CAS乐观锁,JVM是如何实现呢?

    既然synchronized重量级锁是通过系统调用mutex lock来实现,它要发生用户态到内核态的切换,代价太高。那么CAS是怎么实现原子性的呢?它怎么就不需要用户态到内核态的切换?
    cas不需要调用系统命令,所以无需用户态到内核态的切换。那为啥它不需要系统命令呢?因为现代cpu提供了cas原子性指令,帮助了JVM实现。那么提供了这个cas指令,就一定不需要系统调用吗?其实还有个关键特征,就是这个cpu提供的cas指令不是特权指令。如果它是特权指令,只能有操作系统来执行,那么就算有cas指令,JVM用它的话照样会发生用户态到内核态切换,代价照样高。所以,幸好cpu的cas指令不是特权指令,就像普通的load,push等等大多数指令一样,不是特权指令。

    JDK提供CAS的api,具体实现是JVM的JNI来做的。JNI是c++的,它通过直接调用cpu指令来实现。如下:

    UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) {
      oop p = JNIHandles::resolve(obj);
      jint* addr = (jint *)index_oop_from_field_offset_long(p, offset);
      // 调用Atomic.cpp中的cmpxchg()
      return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
    } UNSAFE_END
    

    没有发生系统调用,就达到了原子性了吧!

    相关文章

      网友评论

          本文标题:redis锁

          本文链接:https://www.haomeiwen.com/subject/gwghwhtx.html