ReentrantLock整理

xiaoxiao2025-06-08  15

ReentrantLock内部有一个继承于AQS的Sync类,正如AQS源码里所说的:AQS的子类们应该被定义为非公开的内部辅助类,并且被用来实现它们外部类的其它内部类的同步属性,即ReentrantLock一样,一个Sync内部类实现了AQS的一些同步方法,并且内部公平和非公平锁类调用了这一些方法来进行锁操作。 AQS还为ReentrantLock提供了一个原子值state来表示状态,ReentrantLock定义了改变这个状态值的get和set方法,并且定义这个状态值的意义,用来获取或释放对象。

我们一般都是这样使用ReentrantLock: 1、private final Lock lock = new ReentrantLock() 2、private final Lock lock = new ReentrantLock(Boolean fair) 3、lock.lock() 4、lock.unlock()

构造方法:

首先,第一种方法调用了ReentrantLock的无参构造器,默认创建了一个NonfairSync对象,代码如下:

public ReentrantLock() { sync = new NonfairSync(); }

第二种会按照传的参数来构造公平或非公平锁的对象

public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }

调用lock方法:

首先看内部类NonfairSync:

final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }

1、首先会直接判断State值,若为0,则将其置为1,并且将当前线程置为独享模式的持有者,并取得资源 2、否则会调用AQS中的acquire方法,然后acquire方法会调用NonfairSync中重写的tryAcquire方法来进行尝试获锁的判断

final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); /*首先调用AQS的getState方法来获取state状态,值默认初始为0*/ int c = getState(); /*为0,证明没有线程持有锁,利用原子操作将state+1,并将当前线程设为当前独享模式的持有者,然后返回true*/ if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } }

3、若锁已被当前线程所占有,即状态值不为0,则将当前状态值设置为:当前状态值加上acquires参数(默认为1)。然后返回true,证明再次获锁成功。这块代码为可重入的体现

else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); /*设置状态值为修改后的值*/ setState(nextc); /*成功获锁*/ return true; }

内部类FairSync:

final void lock() { acquire(1); }

这里是直接调用AQS中的acquire方法,然后acquire方法会调用FairSync中重写的tryAcquire方法来进行尝试获锁的判断

protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); /*获取state值*/ int c = getState(); /*公平的体现。若state为0*/ if (c == 0) {

当前节点为等待队列头节点并且能将state设置为acquires代表的值,则成功。这块代码与非公平锁不同的地方就在这,判断当前节点是否为头节点的操作,也是公平的体现,即每次只有队列中的第一个节点才能获取资源

if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } }

若锁已被当前线程所占有,即状态值不为0,则将当前状态值设置为:当前状态值加上acquires参数(默认为1)。然后返回true。这块代码为可重入的体现

else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } }

判断当前节点是否是队列中的第一个节点,是就返回false,不是则返回true

public final boolean hasQueuedPredecessors() { /* 1、若当前队列为空,则h==t,返回false 2、若当前队列只有一个节点,则返回false 3、否则若当前线程所属节点不为第二个节点,返回true;若等于,返回false*/ Node t = tail; Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }

unlock的调用:

public void unlock() { sync.release(1); }

它会调用AQS的release方法,然后release方法调用Sync重写的tryRelease方法来释放资源

protected final boolean tryRelease(int releases) { /*将当前状态值减去releases(默认1)*/ int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); /*标志位,若c=0,则返回true,不为0.返回false*/ boolean free = false; /*若修改后的状态值为0,则证明可以释放,并将持有当前独享模式的线程置空*/ if (c == 0) { /*设置标志位为0*/ free = true; setExclusiveOwnerThread(null); } /*设置修改后的状态值*/ setState(c); /*若free返回true*/ return free; }
转载请注明原文地址: https://www.6miu.com/read-5031501.html

最新回复(0)