一句话总结
谨以此文送给懒得思考源码的新人,包括曾经的自己。
先看构造方法,默认执行非公平锁
//先看构造方法,默认执行非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
执行公平锁的构造方法
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
非公平锁加锁方法
final void lock() {
//线程进来后直接利用CAS尝试抢占锁
if (compareAndSetState(0, 1))
//如果抢占成功state值会被改为1,且设置对象独占锁线程为当前线程
setExclusiveOwnerThread(Thread.currentThread());
//没有抢占成功,执行acquire(1)
else
acquire(1);
}
//点aquire()来到AQS,这里调用了四个方法,这里看先第一个tryAcquire(arg),其他的以后有空再细说。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
此处深坑,用eclipse哪里不会点哪里直通坑底!
tryAcquire(arg)点击后转到AQS里的tryAcquire(arg),但此方法的结果是抛错,一脸懵逼。
其实这里有点套娃,新人和面向对象不太熟悉的人容易在这里晕车。子类NonfairSync里继承了父类AQS的acquire()方法,这个方法又调用了tryAcquire()方法,而这里的tryAcquire()方法子类已经重写,所以并不是执行父类的tryAcquire()方法,
而是调用了子类NonfairSync自己重写的tryAcquire()方法。
其实真正执行的方法就在lock()脚底下
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) {
//第二次比较,利用CAS抢占锁,将当前线程对象设置为执行线程,返回结果true。
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//如果第一次比较发现锁还没释放,那么需要判断正在执行的线程是不是当前对象
else if (current == getExclusiveOwnerThread()) {
//如果是正在执行的线程是当前对象,那么给当前对象的锁状态+1,没报错时返回true
int nextc = c + acquires;
//如果锁状态是负数,说明超过最大锁值,直接抛错
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//如果第一次比较发现有锁并且不是当前对象持有锁,返回false.等待下一次宿命轮回。
return false;
}
尽管非公平锁能确保整体执行效率,但CAS的过程一将功成万骨枯,那些没抢上锁的却会CAS导致额外的资源消耗。
这里就分析完非公平锁的加锁流程。
尽管ReentrantLock源码字里行间都透着对公平锁的深深鄙视,但存在即合理,我们还是需要再看看公平锁的加锁过程。
公平锁对象第一次执行不需要CAS抢锁,而是直接执行acquire(1),在使用tryAcquire()时执行的是自己重写的方法tryAcquire。
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//这里要先确认当前是否排在队列最前,hasQueuedPredecessors()这个方法
//用来判断当前线程在队列中的位置,如果不在队列头部返回true,反之返回false。
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 void unlock() {
sync.release(1);
}
就此一家,别无分号。
点release方法,来到了AQS,这个tryRelease()方法是啥?
public final boolean release(int arg) {
//这里使用tryRelease返回值arg进行判定,如果锁数不为0,则arg为false。
//那么release()方法返回false,解锁失败,否则返回true本次解锁成功。
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
点tryRelease()方法,发现并不是我们需要的方法,这里又和前面那个坑类似,但知道了同样的坑咱不能掉进去两次。
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
回到ReentrantLock的内部类Sync,发现他继承了AQS,
那么他应该继承或重写了tryRelease()方法
果然:
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
//如果执行线程非当前对象线程,抛错
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//如果执行线程是当前对象线程,判断解一次锁后锁状态值,
//如果为0,结束当前线程,锁状态值变为0,返回true
//如果不为0,锁数减1,返回false
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
这里基本完成了ReentrantLock加锁解锁源码分析,希望小白能够从容避坑,愉快玩耍。