共计 3189 个字符,预计需要花费 8 分钟才能阅读完成。
ReentrantLock 底层原理
ReentranLock
是一个支持重入的独占锁,在 java.util.concurrent
包中,底层就是基于 AQS
实现的,所以下面会设计到 AQS 的一些东西,如果还不了解的可以先看这篇 Java AQS 底层原理解析 了解一下。
-
Sync 类
在源码中可以看到
ReentrantLock
类本身并没有继承AQS
,而是创建了一个内部类Sync
来继承AQS
,而ReentrantLock
类本身的那些方法都是调用Sync
里面的方法来实现,而Sync
本身自己也是一个抽象类,它还有两个子类,分别是NonfairSync
和FairSync
,对锁各种实际的实现其实在这两个类中实现,顾名思义,这两个类分别实现了非公平锁和公平锁,在创建ReentrantLock
时可以进行选择。// 默认构造函数是创建一个非公平锁 public ReentrantLock() {sync = new NonfairSync(); } // 接受一个 boolean 参数,true 是创建公平锁,false 是非公平锁 public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}
-
lock()
-
会调用 Sync 类中的 lock()方法,所以需要看创建的是公平锁还是非公平锁
public void lock() {sync.lock(); }
-
非公平锁中的
lock()
方法,先试用CAS
的方式更新AQS
中的state
的状态,默认是 0 代表没有被获取,当前线程就可以获取锁,然后把state
改为 1,接着把当前线程标记为持有锁的线程,如果if
中的操作失败就表示锁已经被持有了,就会调用acquire()
方法final void lock() {if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
-
acquire()
方法是AQS
中的那个方法,这里面调用子类实现的tryAcquire()
方法,最终是调用到Sync
类中的nonfairTryAcquire()
方法,可以看到先判断state
是不是 0,也就是能不能获取锁,如果不能则判断请求锁的线程和持有锁的是不是同一个,如果是的话就把state
的值加 1,也就是实现了重入锁public final void acquire(int arg) {if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt();} protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires); } final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) {if (compareAndSetState(0, acquires)) {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; }
-
重写的
tryAcquire()
方法,上面非公平锁是调用的Sync
里的tryAcquire()
方法,而公平锁则在子类NonfairSync
中重写了这个方法,注意if(c==0)
判断中的代码,也就是线程抢夺锁的时候会调用hasQueuedPredecessors()
方法,这个方法会判断队列中有没有已经先等待的线程了,如果有则当前线程不会抢到锁,这就实现了公平性,上面nonfairTryAcquire()
方法则没有这种判断,所以后来的线程可能会比先等待的线程先拿到锁protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) {if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current); return 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; } public final boolean hasQueuedPredecessors() { // The correctness of this depends on head being initialized // before tail and on head.next being accurate if the current // thread is first in queue. Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
-
-
tryLock()
这个方法是尝试去获取锁,如果成功返回
true
,失败则返回false
。会发现调用的就是上面Sync
中的nonfairTryAcquire()
方法public boolean tryLock() {return sync.nonfairTryAcquire(1); }
-
unlock()
这个方法是释放锁,最终会调用到
Sync
类中的tryRelease()
方法。在这个方法里面会对state
减 1,如果减 1 之后为 0 就表示当前线程持有次数彻底清空了,需要释放锁public void unlock() {sync.release(1); } public final boolean release(int arg) {if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; } protected final boolean tryRelease(int releases) {int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; }
总结:
如果看了 Java AQS 底层原理解析 这篇文章,就会发现其实 JUC
中的很多锁都是在 AQS
的基础上实现的,而且最复杂的逻辑也是在 AQS
中实现的,这些锁只是在它的基础上实现自己的需求。