总结一下ReetrantLock
ReetrantLock分为公平锁,以及非公平锁,默认情况下是非公平锁,分别总结一下这两种使用的异同点。
ReentrantLock 的成员变量有
private final Sync sync;
//这个就是AQS
abstract static class Sync extends AbstractQueuedSynchronizer
首先使用ReetrantLock的时候,会先new一个出来这时候,默认的构造函数是构造一个非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
然后尝试调用加锁的时候,其实调用的是sync的lock方法
public void lock() {
sync.lock();
}
然后就会调用非公平锁的lock方法
final void lock() {
//首先进行cas操作,把state的状态从0变成1,非公平锁和公平锁的区别就是,首先会进行一次CAS操作,如果线程拿到了锁,那就抢占了资源,这样的话,可能新的线程会优先于已经在队列里的那些线程拿到锁,这样不是按顺序的,就是非公平的了
if (compareAndSetState(0, 1))
//如果cas成功了,aqs的持有线程就是当前的线程
setExclusiveOwnerThread(Thread.currentThread());
else
//没有拿到锁,那就调用的AQS中的acquire
acquire(1);
}
如果当前线程没有拿到锁,那么就去执行acquire(1)方法
/**
* 这里有几个步骤
* 1.先尝试获取一下tryAcquire,
* 2.如果获取不到锁,那就将当前线程添加到节点中addWaiter(Node.EXCLUSIVE) 添加到了CLH中
* 3.acquireQueued通过自旋的方式不断的获取锁
* 4.最终拿到了之后会中断自己
* @param arg
*/
public final void acquire(int arg) {
//如果没有获取成功,那么就要添加到队列中了
//再尝试获取一下,如果获取不到,就先加入
if (!tryAcquire(arg) &&
//独占模式,添加了一个,然后返回当前节点
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)){
//当前线程自我中断
selfInterrupt();
}
}
tryAcquire方法最后会调用ReetrantLock的Sync的nonfairTryAcquire方法
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//拿到当前锁的state
int c = getState();
//如果是0的话,就把aqs的state设置为acquires,当前就可以拿到锁了
if (c == 0) {
//尝试一下cas,如果成功就可以拿到锁了
if (compareAndSetState(0, acquires)) {
//设置当前线程为当前lock所拥有的锁
setExclusiveOwnerThread(current);
return true;
}
}
//如果当前的锁就是被这个线程拿到
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//那么就是设置可重入状态了,这个要加
setState(nextc);
return true;
}
//否则当前线程无法获取
return false;
}
其中addWaiter方法
/**
* Creates and enqueues node for current thread and given mode.
* 就是当前线程没有拿到锁的时候
* 1.如果尾部节点有值,那么加到最后一个返回
* 2.如果尾部节点没有值,添加一个节点,然后返回
* @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
* @return the new node
*/
private Node addWaiter(Node mode) {
/**
* 当前线程构建一个node,是独占模式的
* new 出来的这个节点的结构是
* node(当前线程,nextWaiter是个mode)
*/
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
//如果尾节点不为空
if (pred != null) {
//将当前节点的前驱指向尾部节点
node.prev = pred;
//将当前节点设置为尾部节点,使用cas设置一下
if (compareAndSetTail(pred, node)) {
//现在的尾部借点就是 node了,然后再将pred的next指向尾部,然后返回
pred.next = node;
return node;
}
}
//如果尾部节点是空的,就建立一节点,头和尾都指向这个节点,而且自己指向自己[自己的pre指向自己,自己的next指向自己]
enq(node);
return node;
}
private Node enq(final Node node) {
//自旋操作
for (;;) {
//线程A:第一次的时候 tail是null,需要初始化
//线程A:第二次进来的时候,由于是自旋 此时t=head=tail
Node t = tail;
if (t == null) { // Must initialize 必须进行初始化
//尾巴节点是空的,先初始化一下head节点,再将head节点给tail节点
if (compareAndSetHead(new Node()))
tail = head;
} else {
//线程A:第二次进来的时候,由于是自旋,
//当前节点的前驱是t,有可能自己指向了自己
node.prev = t;
//这一步相当于把tail的引用指向了新增的node
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
执行如图
image.png
最后重点的自旋获取锁的方法acquireQueued
网友评论