• java多线程:并发包中ReentrantLock锁的公平锁原理


    一:锁的原理结构

    (1)锁对象内部维护了一个同步管理器的对象AbstractQueuedSynchronizer,AbstractOwnableSynchronizer

    (2)该对象其实是一个抽象类,具体的锁的管理器继承该抽象类

    (3)该抽象类的关键属性有:---->Thread exclusiveOwnerThread(获取锁的线程对象)

    ----> Node head(首节点,正在拥有当前锁的线程构造的Node对象)

    ---->Node tail(尾巴节点,等待获取锁的线程构造的双向链表结构的队列的最后一个节点)

    ---->int state(当前该锁被线程争抢的状态,如果state=0,表示无线程争抢该锁,如果state>0则表示已经有线程拥有该锁)

    二:锁中的Node队列的结构

    (1)所有的线程在执行到获取锁的代码的部分,都会调用同步管理器的lock()方法,如果有线程获取锁,则将该线程构造成node对象,添加到队列尾部,并调用系统命令阻塞当前线程。也就是代码执行到这,当前线程就不再执行。

    (2)拥有锁的线程,在释放锁的时候,会唤醒自己的后继节点的线程,让其争抢锁。

    (3)Node的内部结构属性

    --->int waitStatus(当前node的线程在争抢锁过程的状态标识)

    --->Node prev(当前node的上一个node的引用,前驱节点)

    --->Node next(当前node的下一个INITALnode的引用,后继节点)

    --->Thread thread(当前node所代表的线程的线程对象)

    --->Node nextWaiter(下一个等待着的node)

    (4)node等待状态的的含义

    CANCELLED =  1:由于在同步队列中等待的线程等待超时或被中断,需要从同步队列中取消等待,节点进入该状态不会再变化

    SIGNAL    = -1:后继节点线程处于等待状态,而当前节点的线程如果释放了同步状态或者被取消,将会通知后继节点,使后继节点得以运行。

    CONDITION = -2:在等待队列中,节点线程等待在Condition上,当其他线程对Condition调用了signal()方法后,该节点将会从等待队列中转移到同步队列中。加入到对同步状态的获取中。

    PROPAGATE = -3:表示下一次共享式同步状态获取将会无条件被传播下去。

    INITIAL=0:初始化状态

    三:公平锁和非公平锁的区别

    (1)公平锁的锁获取,严格按照等待顺序进行锁获取,在获取锁的时候有一个判断hasQueuedPredeccssors(),同步队列中是否有前驱节点在等待获取锁,如果有,则放弃获取锁,而是添加到队尾,排队获取锁

    (2)非公平锁,是获取锁的顺序是随机的,甚至,有的线程可能会一直无法获取锁,出现线程饥饿情况。

    (3)公平锁的性能往往没有非公平锁的性能高,因为它需要排队,则需要进行程序状态的切换,要比非公平锁的切换次数多。

    (4)公平锁:在获取锁的时候,先检查是否已经有队列形成。如果有,则加入队列。按顺序排队,获取锁。

    (5)非公平锁:在获取锁的时候,会多次尝试获取锁,而不着急加入队列排队,更有主动权获取锁。如果多次获取也没获取成功,也加入排队,此时和公平锁一样。其两者的区别在于:非公平锁,加大了新晋线程抢到锁的概率。

    四:锁的获取和释放的过程图

    【LockSupport】工具的api

    park():阻塞当前线程,如果调用unpark(Thread thread)方法或者当前线程被中断,才能从park()方法返回。

    parkNanos(long nanos):阻塞当前线程,最长不超过nanos纳秒,返回条件在park()的基础上增加了超时返回。

    parkUntil(long deadline):阻塞当前线程,直到deadline时间(从1970年开始到deadline时间的毫秒数)

    unpark(Thread thread):唤醒处于阻塞状态的线程thread

    park(Object blocker):阻塞当前线程,如果调用unpark(Thread thread)方法或者当前线程被中断,才能从park(Object blocker)方法返回。blocker用于问题排查和系统监控。

    parkNanos(Object blocker,long nanos):阻塞当前线程,最长不超过nanos纳秒,返回条件在park(Object blocker)的基础上增加了超时返回。blocker用于问题排查和系统监控。

    parkUntil(Object blocker,long deadline):阻塞当前线程,直到deadline时间(从1970年开始到deadline时间的毫秒数),blocker用于问题排查和系统监控。

    ======================公平锁和非公平锁=================

     转载:全文地址请点击:https://blog.csdn.net/qyp199312/article/details/70598480?utm_source=copy 

    公平锁:

    • 公平和非公平锁的队列都基于锁内部维护的一个双向链表,表结点Node的值就是每一个请求当前锁的线程。公平锁则在于每次都是依次从队首取值。
    • 锁的实现方式是基于如下几点:
      • 表结点Node和状态statevolatile关键字。
      • sum.misc.Unsafe.compareAndSet的原子操作(见附录)。

    非公平锁:

    • 在等待锁的过程中, 如果有任意新的线程妄图获取锁,都是有很大的几率直接获取到锁的。

    ReentrantLock锁都不会使得线程中断,除非开发者自己设置了中断位。
    ReentrantLock获取锁里面有看似自旋的代码,但是它不是自旋锁。
    ReentrantLock公平与非公平锁都是属于排它锁。

    公平锁和非公平锁

    ReentrantLock 的公平锁和非公平锁都委托了 AbstractQueuedSynchronizer#acquire 去请求获取。

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    • tryAcquire 是一个抽象方法,是公平与非公平的实现原理所在。
    • addWaiter 是将当前线程结点加入等待队列之中。公平锁在锁释放后会严格按照等到队列去取后续值而非公平锁在对于新晋线程有很大优势
    • acquireQueued 在多次循环中尝试获取到锁或者将当前线程阻塞。
    • selfInterrupt 如果线程在阻塞期间发生了中断,调用 Thread.currentThread().interrupt() 中断当前线程。

    ReentrantLock 对线程的阻塞是基于 LockSupport.park(this); (见 AbstractQueuedSynchronizer#parkAndCheckInterrupt)。 先决条件是当前节点有限次尝试获取锁失败。

    公平锁和非公平锁在说的获取上都使用到了 volatile 关键字修饰的state字段, 这是保证多线程环境下锁的获取与否的核心。
    但是当并发情况下多个线程都读取到 state == 0时,则必须用到CAS技术,一门CPU的原子锁技术,可通过CPU对共享变量加锁的形式,实现数据变更的原子操作。
    volatile 和 CAS的结合是并发抢占的关键。

    CAS和volatile, Java并发的基石

    volatile 是Java语言的关键字, 功能是保证被修饰的元素(共享变量):

    任何进程在读取的时候,都会清空本进程里面持有的共享变量的值,强制从主存里面获取;
    任何进程在写入完毕的时候,都会强制将共享变量的值写会主存。
    volatile 会干预指令重排。
    volatile 实现了JMM规范的 happen-before 原则。

    在多核多线程CPU环境下, CPU为了提升指令执行速度,在保证程序语义正确的前提下,允许编译器对指令进行重排序。也就是说这种指令重排序对于上层代码是感知不到的,我们称之为 processor ordering.

    JMM 允许编译器在指令重排上自由发挥,除非程序员通过 volatile等 显式干预这种重排机制,建立起同步机制,保证多线程代码正确运行。见文章:Java并发:volatile内存可见性和指令重排

    当多个线程之间有互相的数据依赖的之后, 就必须显式的干预这个指令重排机制


    CAS是CPU提供的一门技术。在单核单线程处理器上,所有的指令允许都是顺序操作;但是在多核多线程处理器上,多线程访问同一个共享变量的时候,可能存在并发问题。

    使用CAS技术可以锁定住元素的值。Intel开发文档, 第八章
    编译器在将线程持有的值与被锁定的值进行比较,相同则更新为更新的值。
    CAS同样遵循JMM规范的 happen-before 原则。
    JAVA CAS原理深度分析博客

    公平锁和非公平锁在说的获取上都使用到了 volatile 关键字修饰的state字段, 这是保证多线程环境下锁的获取与否的核心。
    但是当并发情况下多个线程都读取到 state == 0时,则必须用到CAS技术,一门CPU的原子锁技术,可通过CPU对共享变量加锁的形式,实现数据变更的原子操作。
    volatile 和 CAS的结合是并发抢占的关键。

    --------------------- 本文来自 平菇虾饺 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/qyp199312/article/details/70598480

  • 相关阅读:
    poj--2031--Building a Space Station(prime)
    nyoj--364--田忌赛马(贪心)
    nyoj--496--巡回赛(拓扑排序)
    nyoj--1100--WAJUEJI which home strong!(bfs)
    hdoj--5625--Clarke and chemistry(枚举)
    poj--1753--Flip Game(dfs好题)
    poj--1101--The Game(bfs)
    冒泡排序
    php常见错误
    php乱码
  • 原文地址:https://www.cnblogs.com/shangxiaofei/p/5803533.html
Copyright © 2020-2023  润新知