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)公平锁的性能往往没有非公平锁的性能高,因为它需要排队,则需要进行程序状态的切换,要比非公平锁的切换次数多。

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

时间: 08-21

java多线程:并发包中ReentrantLock锁的公平锁原理的相关文章

多线程编程-- part5.1 互斥锁之公平锁-获取锁

基本概念 1.AQS:AbstractQueuedSynchronizer类 AQS是java中管理“锁”的抽象类,锁的许多公共方法都是在这个类中实现.AQS是独占锁(例如,ReentrantLock)和共享锁(例如,Semaphore)的公共父类. (01) 独占锁 -- 锁在一个时间点只能被一个线程锁占有.根据锁的获取机制,它又划分为“公平锁”和“非公平锁”.公平锁,是按照通过CLH等待线程按照先来先得的规则,公平的获取锁:而非公平锁,则当线程要获取锁时,它会无视CLH等待队列而直接获取锁.

【试验局】ReentrantLock中非公平锁与公平锁的性能测试

硬件环境: CPU:AMD Phenom(tm) II X4 955 Processor Memory:8G SSD(128G):/ HDD(1T):/home/ 软件环境: OS:Ubuntu14.04.3 LTS Java:JDK1.7 关于ReentrantLock中非公平锁和公平锁详细区别以及实现方式在这里不再叙述,有关ReentrantLock的源码解析参照. 首先我们用实例验证,非公平锁以及公平锁是否是其介绍的那样,非公平锁在获取锁的时候会首先进行抢锁,在获取锁失败后才会将当前线程加

java多线程知识汇总(三)如何选择锁?如何加锁

1.锁,保证的是被锁的代码,一次执行完毕才能被其他线程执行,锁保证了一个线程执行过程中不被其他线程打断.以保证数据的准确性. 2.数据的读写过程,是有冲突的,当一个线程正在读数据,另一个线程正在写同一个数据,就有可能导致数据不准确,造成脏数据.就要保证读写分开时间段.即加锁. 3.如果想要保证共享对象的一个方法按序执行,则在这个方法上加锁. 4.多个方法加同一个锁:在多个方法上加同一个锁.如果想保证读写能读取到准确数据,则在同一成员变量的对应读写方法上加同一个锁,同一个锁.保证读写正常. 5.同

多线程编程-- part5.1 互斥锁之公平锁-释放锁

释放公平锁 1.unlock() unlock()在ReentrantLock.java中实现的,源码如下: public void unlock() { sync.release(1); } 说明:unlock()是解锁函数,它是通过AQS的release()函数来实现的.在这里,“1”的含义和“获取锁的函数acquire(1)的含义”一样,它是设置“释放锁的状态”的参数.由于“公平锁”是可重入的,所以对于同一个线程,每释放锁一次,锁的状态-1. 2.release() release()在A

Java多线程系列--“JUC线程池”03之 线程池原理(二)

线程池示例 在分析线程池之前,先看一个简单的线程池示例. import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; public class ThreadPoolDemo1 { public static void main(String[] args) { // 创建一个可重用固定线程数的线程池 ExecutorService pool = Executors.newFixedThre

Java多线程系列--“JUC线程池”02之 线程池原理(一)

ThreadPoolExecutor简介 ThreadPoolExecutor是线程池类.对于线程池,可以通俗的将它理解为"存放一定数量线程的一个线程集合.线程池允许同时运行的线程数量就是线程池的容量:当添加到线程池中的线程超过它的容量时,会有一部分线程阻塞等待.线程池会通过相应的调度策略和拒绝策略,对添加到线程池中的线程进行管理." ThreadPoolExecutor数据结构 ThreadPoolExecutor的数据结构如下图所示: 各个数据在ThreadPoolExecutor

Java多线程系列--“JUC线程池”05之 线程池原理(四)

拒绝策略介绍 线程池的拒绝策略,是指当任务添加到线程池中被拒绝,而采取的处理措施.当任务添加到线程池中之所以被拒绝,可能是由于:第一,线程池异常关闭.第二,任务数量超过线程池的最大限制. 线程池共包括4种拒绝策略,它们分别是:AbortPolicy, CallerRunsPolicy, DiscardOldestPolicy和DiscardPolicy. AbortPolicy -- 当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException 异常. Calle

Java多线程系列--“JUC线程池”04之 线程池原理(三)

本章介绍线程池的生命周期. 线程有5种状态:新建状态,就绪状态,运行状态,阻塞状态,死亡状态.线程池也有5种状态:然而,线程池不同于线程,线程池的5种状态是:Running, SHUTDOWN, STOP, TIDYING, TERMINATED. 线程池状态定义代码如下: /** * The main pool control state, ctl, is an atomic integer packing * two conceptual fields * workerCount, indi

Java多线程12:ReentrantLock中的方法

公平锁与非公平锁 ReentrantLock有一个很大的特点,就是可以指定锁是公平锁还是非公平锁,公平锁表示线程获取锁的顺序是按照线程排队的顺序来分配的,而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,先来的未必就一定能先得到锁,从这个角度讲,synchronized其实就是一种非公平锁.非公平锁的方式可能造成某些线程一直拿不到锁,自然是非公平的了.看一下例子,new ReentrantLock的时候有一个单一参数的构造函数表示构造的是一个公平锁还是非公平锁,传入true就可以了: publ