java的reentrantlock及同步等待队列

what:

  线程的多种状态,如下:

    第一种:初始(NEW)指新建一个线程对象如Thread thread = new Thread(()->{});还没有调用其start() 方法。

    第二种:运行状态(RUNNABLE)java线程中将就绪状态(ready,满足条件但未获取时间片)和运行中(running)两种状态笼统的称为“运行”。

        线程对象在创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获取cpu时间片后将会变为运行中状态(running)。

    第三种:阻塞状态(BLOCKED):表示线程阻塞于锁

    第四种:等待状态(WAITING):进入该状态的线程需要等待其他线程做一些特定的动作(通知或中断)。

    第五种:超时等待(TIME_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。

    第六种:终止状态(TERMINATED):表示该线程已经被执行完毕。

 

 

 

  Lock:

    java中可以根据Lock接口用来实现自定义锁对象,可是当进行锁对象的自定义时,通常要在类内部内置一个内部类,而这个内部类须要实现队列同步器AQS(AbstractQueuedSynchronizer),而且重写其中的方法。当调用自定义锁的时候,实际上是关联到了内部类的队列同步器(AQS)的方法中,从而实现了自定义对象的锁功能。

    注意:reentrantlock也是据于AQS实现的。

 

  同步队列和等待队列(以reentrantlock为例,并主讲公平锁):

    公平锁而言,等待队列和同步队列都是一个FIFO队列;而非公平锁来讲,同步队列没法保证FIFO特性。

    同步队列:存储的是竞争锁线程节点(注意:这些竞争锁的线程都是活动的),每个线程都是在不断的自旋检查获取锁的条件,来尝试着获取锁。

        原因:一个锁的获取是具备排他性的(固然例如读取锁这种共享锁不在讨论范围以内),那么当多个线程来竞争锁的时候,就须要对这些线程来进行排队。

    等待队列:存储的也是线程节点,这些线程节点指向正在等待被唤醒的线程,能够简单理解为等待队列中的线程节点指向的都是正在沉眠的线程。

    

How:

  reentrantlock如何管理的同步、等待队列

    reentrantlock是基于同步器AQS实现的,在AQS中可以存在一个或者多个ConditionObject。1个ConditionObject管理1个单向链表的等待队列;而1个AQS自己管理一个双向的同步队列,如下图:

 

 

       当一个线程执行了wait方法以后,该线程会释放本身掌握的锁对象,而且将本身封装成一个Node节点,加入到对应condition的等待队列的尾节点以后。demo如下:

 

 

       

  等待队列和同步队列是如何配合的

    概述:等待队列就像队列名字同样,存储的是全部在condition上进行等待的线程Node。当线程被唤醒后,线程并非立刻就获得对象的使用权,而是进入到同步队列中进行排队来使用锁

    Condition是Lock的一个内部类,经过condition来进行等待以及通知操做。

      当执行“condition.await()”,就会进行上面咱们说的:将节点加到了等待队列的尾部。

      当执行“condition.signal()”进行通知操做,会发生下面的操作:等待队列中的首节点就会出队,而后将本身封装成为一个同步队列的节点,并将本身加入同步队列尾部参与到对象的获取过程当中去。

      Object对象中存在notifyAll方法,用来通知全部等待锁释放的线程,那么Condition中也有相应的方法condition.signalAll()。操作为:将等待队列中的全部节点所有封装成为同步队列的节点并添加到同步队列中。

 

 

 

      

  同步队列中的线性如何获得锁

    对于“公平锁”:同步队列是一个双向的FIFO队列,FIFO特性决定了只有队列头节点才是可以出队的节点,而当节点出队时意味着节点指向的线程已经得到了锁的使用权

    具体方式:同步队列的所有线程节点都会自旋进行检查,检查过程是在同时进行判断和获取,判断的条件是:当前的节点的上一个节点是否存在,当存在时意味着本身并非头节点,出队的条件,天然就不能获取锁,当该节点以已经没有节点了,就会去尝试获取锁,只有获取锁成功时才能出队

 

    为何头节点要尝试获取锁,成功后才出队:

      当头节点获取到锁并出队时,线程可能在出队后还持有锁,并未释放,可是出队后,这个节点就再也不是头节点了。当前队列的头节点由于不能肯定必定能够获取锁,因此就须要加判断了

 

    对于“非公平锁”,和上面类似,只是:不需要判断自己处于队头。即:同步队列中的所有线程节点都在获取锁的使用权,谁可以获取到,就算谁的。

      “非公平锁”,会出现线程的“饥饿”问题,即:可能一个线程等待和很长时间以后都不能获取到锁。

 

    注意:

      ReentrantLock默认就是非公平锁。原因是:非公平锁相较公平锁而言,进行上下文切换的次数更少吞吐量更大。背后原因:由于当一个线程释放锁以后,若是一次尝试获取锁,获取的几率要大不少。

 

posted @ 2022-06-02 15:11  修心而结网  阅读(717)  评论(0编辑  收藏  举报