首先,想先梳理一下自己看源码的目的,最近有篇文章特别火《程序员12小时惊魂记:凌晨迁移数据出大事故!》,里面强调了解决问题的能力很重要,这给我一种只看源码和设计模式关键时刻不给力的感觉,周末去看了鲁能和恒大的比赛,那种在瞬间做出决策的压力只会比事故现场更紧张,而且不能有一点犹豫。我想竞技体育的魅力,就在于此吧,要把所有的技能都训练到潜意识层面,才会在瞬间爆发出来。所以,要不断的研究不断的实践,弄懂原理,融汇贯通。
看源码步骤:
1、选中类名右击,选中Giaggrams->Show Diagram查看类关系图。
2、在类关系图中右击,选中Show Categories->Methods等查看类属性和方法等。可以看到ReentrantLock实现了Lock,Serializable两个接口,有Sync、NonfairSync和FairSync三个内部类,NonfairSync和FairSync继承Sync类,Sync继承和ReentrantLock同一个包下的AbstractQueuedSynchronizer抽象类。
3、在自己的测试工程中写一个测试类,创建ReentrantLock对象,调用其中的方法。
public class ReentrantLockTest {
public static void main(String[] args){
ReentrantLock reentrantLock = new ReentrantLock();
reentrantLock.lock();
reentrantLock.unlock();
}
}
(1)从构造函数开始看,无参构造方法中只是实现了sync = new NonfairSync(),即默认使用非公平锁。
(2)常用的方法lock(),实现很简单,只是调用了sync.lock(),抽象方法,由子类实现。查看默认的非公平锁的实现。
final void lock(){
if (compareAndSetState(0,1)){
setExclusiveOwnerThread(Thread.currentThread());
} else{
acquire(1);
}
}
public final void acquire(int arg){
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE),arg)){
selfInterrupt();
}
}
- setExclusiveOwnerThread把当前线程设置成持有锁的线程。
- 如果CAS失败,说明锁已经被别人获取,那么通过acquire()尝试去获取锁。
- 其中的抽象类的非抽象方法,AbstractQueuedSynchronizer.java中tryAcquire方法,在抽象类中只是简单了抛出了异常,这种主要是由子类去实现,要去看不同的子类都是怎么实现的该方法。这个方法中,由FairSync公平锁和NonFairSync非公平锁的来实现此方法。
- tryAcquire方法以独占的方式获取锁,说白了就是,再获取一遍锁的状态,如果还是没人获取,就用CAS再获取一遍,如果有人获取了,恰巧是自己线程,那就把状态+1,其他情况都是获取锁失败,这时候线程就会进入等待队列。下面看下非公平锁实现的tryAcquire的实现。
- 在NonFairSync非公平锁中调用nonfairTryAcquire(acquires);
final boolean nonfairTryAcquire(int acquires){
final Thread current = Thread.currentThread();
int c = getStatus();
if (c == 0){
if (compareAndSetState(0,1)){
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;
}
- getStatus()获取的状态,就是上面CAS设置的状态,如果状态(c ==
0)说明当前没有任何线程获取到锁,那就再获取一遍锁,设置线程,返回true; - current == getExclusiveOwnerThread()说明当前线程重入。
- setState(),把重入次数+1更新到status,返回true说明获取到这个锁。
- 继续看acquire()方法中的判断条件,addWaiter(Node.EXCLUSIVE)中 EXCLUSIVE定义 = null; 注释中表明,此标记表示节点在独占模式下等待。
private Node addWaiter(Node mode){
Node node = new Node(Thread.currentThread(),mode);
Node pred = tail;
if (pred != null){
node.prev = pred;
if (compareAndSetTail(pred,node)){
pred.next = node;
return node;
}
}
enq(node);
return node;
}
private Node enq(final Node node){
for (;;){ // me:原来必须初始化是这样写
Node t = tail;
if (t == null){ //Must initialize
if (compareAndSetHead(new Node())){
tail = head;
}
} else {
node.prev = t;
if (compareAndSetTail(t,node)){
t.next = node;
return t;
}
}
}
}
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
static final class Node{
...
volatile Node prev;
volatile Node next;
volatile Thread thread;
Node nextWaiter;
Node(Thread thread, Node mode){
this.nextWaiter = mode;
this.thread = thread;
}
...
}
}
- pred != null,说明当前链表不为空,则把node追加到链表的尾部,并返回node。
- addWaiter()方法保证把Node放到一个链表里去,并返回尾节点。
- 继续看acquire()方法中的判断条件,acquireQueued(addWaiter(Node.EXCLUSIVE),arg))
final boolean acquireQueued(final Node node, int arg){
boolean failed = true;
try {
boolean interrupted = false;
for (;;){
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)){
setHead(node);
p.next = null; //help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p,node) &&
parkAndCheckInterrupt()){
interrupted = true;
}
}
} finally {
if (failed)
cancleAcquire(node);
}
}
- p == head是链表的特性,head节点不存数据,所以如果当前节点的前驱节点为head时,则移除掉原head,把当前node节点设置为head。
- shouldParkAfterFailedAcquire()里面有信号量,第一次执行结果为false,且设置信号量,继续执行for的死循环,使用信号量,将线程挂起,然后等待一直线程被唤起。
- finally表示如果执行过程中有异常情况,则取消当前线程获取锁的动作。
- for循环,从传入的尾节点开始,不断的获取前驱节点,只有在获取到head节点时,才能走到唯一的出口
- 继续看acquire(),如果获取锁过程中有中断标记,则中断当前线程。