结论
先给出结论。综合起来,ReentrantLock是一个独占锁,可重入锁,且有公平锁和非公平锁之分。
公平锁和非公平锁各自是怎么实现的呢?非公平锁中,某一线程调用lock()时就会使用CAS尝试获取锁(不管锁当前是否空闲),获取失败才会加入到队列中等待。而在公平锁中,某一线程调用lock()时,只有在当前锁是空闲时,且等待队列中没有其他线程才会使用CAS去获取,如果等待队列中有其他线程,那么它还是会加入到队列中,然后按顺序获取。那么我们可以想到,非公平锁的性能是肯定要比公平锁要好的,因为可能一个线程调用lock的时候,锁可能刚好释放,那么就可以被当前线程占用。若使用公平锁,则还需要去队列中通知下一个等待的线程,唤醒线程也是需要一定的时间的。也可能存在这样一种情况,当唤醒该线程的时候,刚好前面插队的线程已经用完锁并且释放了。
而重入又是怎么实现的呢?可以看到syn继承了AQS,而AQS又继承了AbstractOwnableSynchronizer。在AbstractOwnableSynchronizer中有一个用于记录独占式获取锁时,最近一次获取锁的线程的字段。
public abstract class AbstractOwnableSynchronizer
implements java.io.Serializable {
/**
* The current owner of exclusive mode synchronization.
*/
private transient Thread exclusiveOwnerThread;
然后在ReentrantLock 只需要判断调用lock()的线程是否和这里面记录的线程相等,若相等,则表明该线程要再次获取锁,ReentrantLock就将它的state+1(state 是实际上通过AQS来维护的),用来记录重入的次数。在release()的时候,state-1。state=0的时候,才是表示完全释放锁的时候,别的线程才能去获取锁。
还需要注意的一点是,非公平锁和公平锁的tryLock()方法都是在调用时若锁可用,则用CAS去尝试获取,而忽略等待队列。但是对于tryLock(long timeout, TimeUnit unit)方法则是调用各自的tryAcquire()。所以总结起来就是在公平锁中也是有非公平地获取锁的方式的,也算是留个后路吧,毕竟非公平方式的性能更高
ReentrantLock
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
//默认非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
//获取锁,非公平锁和公平锁实现不同。具体看下面各自的方法
public void lock() {
sync.lock();
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
//尝试使用非公平的方式获取锁,调用的是Sync里面的方法(不区分是公平还是不公平),我的理解是可能给公平锁一个非公平获取锁的途径。即使是在公平锁中,如果当前锁是空闲的,那么会立即获取到该锁,不管该锁的等待队列中是否有线程在等待。
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
//该方法实际上调用的是各自锁里面的tryAcquire()方法,跟上面的tryLock()不一样,可以保证公平性
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public void unlock() {
sync.release(1);
}
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
public boolean isLocked() {
return sync.isLocked();
}
//只保留了一些关键方法
......
public String toString() {
Thread o = sync.getOwner();
return super.toString() + ((o == null) ?
"[Unlocked]" :
"[Locked by thread " + o.getName() + "]");
}
}
Sync(定义了公平锁和非公平锁都要实现的方法)
继承AQS,定义了一些基本的方法,比如lock()等,然后分别由公平锁和非公平锁去实现
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
//子类各自实现
abstract void lock();
//对于2种锁的tryLock()方法,都是调用该方法,tryLock()其实就是尝试获取锁,即在调用该方法的时候就用CAS获取看看能否获取成功,如果不能获取成功,则返回false
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()) {
//保证重入,重入成功则state+1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
//尝试释放,因为是可重入的,所以调用了之后还不一定可以释放锁,所以需要根据state来判断(state可以说是记录了重入次数)
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
//如果调用释放的锁不是当前占用的锁,那肯定是出错了
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {//如果状态=0,表示这次释放完之后,锁就完全释放了,然后将free=true表示锁已经空闲
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
// Methods relayed from outer class
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
}
然后是非公平锁的实现
非公平锁NonfairSync
//非公平锁
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
非公平锁在某一线程调用Lock方法时就尝试用CAS获取,而不用通过队列,进而提高效率,若获取失败,则调用AQS的acquire,实际上AQS的acquire方法内部调用的是NonfairSync自己实现的tryAcquire()
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
然后是公平锁的实现
公平锁FairSync
static final class FairSync extends Sync {
//公平锁在尝试获取锁时,只是简单的判断该锁是否释放,以及锁等待队列中有没有等待的线程,要是没有,则自己获取锁
final void lock() {
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//只有锁是空闲的时候才会尝试去获取,先判断等待队列中是否有线程,如果没有则用CAS去获取
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;
}
//两者都不满足就获取失败,然后在AQS的acquire()里面会调用tryAcquire()。当tryAcquire()返回false的时候,当前线程就会被加入等待线程队列
return false;
}
}
再和代码结合起来看结论:综合起来,ReentrantLock是一个独占锁,可重入锁,且有公平锁和非公平锁之分。
公平锁和非公平锁的区别则是,非公平锁中,某一线程调用lock()时就会使用CAS尝试获取锁(不管锁当前是否空闲),获取失败才会加入到队列中等待。而在公平锁中,某一线程调用lock()时,只有在当前锁是空闲时,且等待队列中没有其他线程才会使用CAS去获取,如果等待队列中有其他线程,那么它还是会加入到队列中,然后按顺序获取。那么我们可以想到,非公平锁的性能是肯定要比公平锁要好的,因为可能一个线程调用lock的时候,锁可能刚好释放,那么就可以被当前线程占用。若使用公平锁,则还需要去队列中通知下一个等待的线程,唤醒线程也是需要一定的时间的。也可能存在这样一种情况,当唤醒该线程的时候,刚好前面插队的线程已经用完锁并且释放了。
而重入又是怎么实现的呢?可以看到syn继承了AQS,而AQS又继承了AbstractOwnableSynchronizer。在AbstractOwnableSynchronizer中有一个用于记录独占式获取锁时,最近一次获取锁的线程的字段。
public abstract class AbstractOwnableSynchronizer
implements java.io.Serializable {
/**
* The current owner of exclusive mode synchronization.
*/
private transient Thread exclusiveOwnerThread;
然后在ReentrantLock 只需要判断调用lock()的线程是否和这里面记录的线程相等,若相等,则表明该线程要再次获取锁,ReentrantLock就将它的state+1(state 是实际上通过AQS来维护的),用来记录重入的次数。在release()的时候,state-1。state=0的时候,才是表示完全释放锁的时候,别的线程才能去获取锁。
还需要注意的一点是,非公平锁和公平锁的tryLock()方法都是在调用时若锁可用,则用CAS去尝试获取,而忽略等待队列。但是对于tryLock(long timeout, TimeUnit unit)方法则是调用各自的tryAcquire()。所以总结起来就是在公平锁中也是有非公平地获取锁的方式的,也算是留个后路吧,毕竟非公平方式的性能更高

本文详细探讨了ReentrantLock的实现原理,包括其作为独占锁、可重入锁的特点,以及公平锁与非公平锁的工作机制。文章还介绍了如何通过CAS实现重入,并对比了公平锁与非公平锁在性能上的差异。
170万+

被折叠的 条评论
为什么被折叠?



