标签:进入 入队 int 参数 比较 修改 表示 节点 boolean
ReentrantLock是一个可重入的独占锁,同时只能有一个线程可以获取该锁,其他获取该锁的线程会被阻塞而放入该锁的AQS阻塞队列里面。ReentrantLock最终是使用AQS来实现的,并且根据参数来决定其内部是一个公平锁还是非公平锁,默认是非公平锁。
public ReentrantLock(){ sync = new NonfairSync(); } public ReentrantLock(boolean fair){ sync = fair ? new FairSync() : new NonfairSync(); }
Sync类直接继承自AQS,它的子类NonfairSync和FairSync分别实现了获取锁的非公平和公平策略。AQS中的state状态值标识线程获取该锁的可重入次数,默认情况下,state=0表示没有线程获取锁,当一个线程获取到锁的时候,会尝试使用CAS修改state的值+1,如果该线程获取成功,就记录该锁的持有者是当前线程,当这个线程在没有释放锁的情况下第二次获取该锁之后,state状态值被设置成2,也就是重入的次数;当线程释放锁的时候,释放一次,state-1,直到state变成0之后,该锁才能被其他线程再次获取。
public void lock(){ sync.lock(); }
可以看到ReentrantLock获取锁委托给了sync的lock方法,sync的实现,由传入的参数确定是公平锁实现还是非公平锁实现;
首先先看一下非公平锁的实现:
final void lock(){ if(compareAndSetState(0,1){ setxxxxx(Thread.currentThread); //cas修改state成功,设置锁持有者是当前线程 }else{ acquire(1); //cas修改state失败,调用AQS的acquire,再次尝试获取锁,如果还 //不成功,放入AQS阻塞队列; } }
acquire方法里面,比较核心的是调用了tryAcquire(int args)方法,
AQS没有定义tryAcquire的实现,它需要子类自己定制化,ReentrantLock的重写tryAcquire方法如下:
可以看到,在这个方法里,会再次尝试着cas修改state状态获取锁,如果失败了,就返回false,放到AQS阻塞队列中。
非公平锁的体现:如果线程A进入tryAcquire方法的代码(4)时,state != 0,那么线程A就放到了AQS的阻塞队列中;后面线程B进入了tryAcquire方法中,进入到代码(4)时,发现state=0了,那么线程B就可以直接获取锁。虽然线程A先执行的,线程B是后执行的,但是获取到锁的却是后来的线程B,这就是非公平锁的体现。
其次是公平锁的实现:
公平性策略的实现主要依赖于:hasQueuedPredecessors()方法:
返回false,即该线程可以获取锁;否则,该线程不能获取锁,由阻塞队列的头结点获取锁。【公平锁的体现】;
如果当前线程节点有前驱节点则返回true,否则如果当前AQS队列为空或者当前线程节点是AQS的第一个节点则返回false(即:该线程可以获取锁)。其中如果h==t 则说明当前队列为空,直接返回false;如果h!=t并且s==null,说明有一个线程要作为AQS的第一个节点入队列,那么返回true;如果h!=t并且s!=null和s.thread!=Thread.currentThread()则说明队列里的第一个元素不是当前线程,那么也返回true。
ReentrantLock是独占锁,同时只能有一个线程获取该锁,而实际中会有读多写少的场景,ReentrantLock满足不了这个需求,所以有了ReentrantReadWriteLock类,它可以允许多个线程可以同时获取读锁。它的内部维护了一个ReadLock和WriteLock,他们依赖Sync实现具体功能,也同样提供了公平锁和非公平锁。但是AQS中只维护了一个state状态,一个state怎么标识读和写两种状态?ReentrantReadWriteLock使用了state的高16位表示读状态,低16位表示写状态。
ReentrantLock原理、ReentrantReadWriteLock原理
标签:进入 入队 int 参数 比较 修改 表示 节点 boolean
原文地址:https://www.cnblogs.com/anduodefeng/p/13156746.html