Reentrantlock类中大约共有21个方法,三个内部类(Syn,FairSyn,NonfairSync),实现了接口Serializable,主要的加锁解锁功能,是通过三个内部类完成的。在所有的方法中涉及到加锁的方法有三个,lock(),tryLock(),tryLock(long,TimeUnit ),涉及到解锁的一个unlock().与Condition有关的共四个方法(后续介绍),其他的函数基本上是Getter函数。
流程图:
- lock()
先看一下啊lock的源码,sync是什么?查看声明, private final Sync sync;sync是Sync的一个对象,其中Sync就是前面提到的三个内部类的其中之一,也是另外两个类的父类,Sync继承了一个听说很重要的类AbstractQueuedSynchronizer,没有具体深入,待后续跟进。
public void lock() {
sync.lock();
}
lock()在Sync中是一个抽象函数,具体的定义延迟到了子类中,这样子类可以更加灵活的去实现它,这里用到了一个设计模式,模版设计模式父类声明子类实现父类调用。这里lock()函数实现的子类就是ReentrantLock中的另外两个内部类FairSyn,NonfairSync,以NonfairSync内部类进行剖析。
final void lock() {
//CAS操作,将state设为1,state是用来表示有无锁竞争
if (compareAndSetState(0, 1))
//获取锁成功,将当前的锁的所有现场设为当前线程
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);//获取失败调用acquire试图再次请求或者阻塞自己
}
acquire()是来自AbstractQueuedSynchronizer类具体实现如下
public final void acquire(int arg) {
//再一次尝试获取锁若成功则返回,其中tryAcquire的实现实在子类中,若获取失败则调用acquireQueued()将加入到队列中的当前线程阻塞
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
下面是非公平锁的tryAcquire()具体实现,间接调用了nonfairTryAcquire(),首先获取state,判断当前有无线程占有该锁,c==0无竞争,通过CAS操作修改当前的state,如果c!=0在判断当前锁占有线程是不是当前线程,若是不用在获取锁,直接跟新state,此时不许用cas操作,线程安全,偏向锁。如果全部失败返回false,调用acquireQueued()将加入到队列中的当前线程阻塞。
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;
}
下面是addWaiter()的函数实现,Node类是AbstractQueuedSynchronizer的一个内部类,个人理解是用来维护整个阻塞队列的。首先通过当前线程构造一个Node,获取链表的末尾节点,判断末尾节点是否存在,若pred!=null,则CAS操作直接将node插入到末尾节点后面,这里的链表是一个双向链表。若末尾节点为空,则调用enq()函数,将当前节点加入到队列中,具体看enq函数的源码。
private Node addWaiter(Node mode) {
//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;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {//无限循环,确保当前节点入队列
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;
}
}
}
}
将当前线程入队列之后,因为获取不要锁,则需要将当前线程阻塞,acquireQueued()源码如下,在试图阻塞线程之前,每次都要重新获取一下锁,万一成功了呢,看似会死循环,我们来看一下shouldParkAfterFailedAcquire(p, node) 和parkAndCheckInterrupt()都做了什么
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 设置为空帮助GC回收
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
shouldParkAfterFailedAcquire()(ps:这里我把原来的英文注释删掉了,如果你英文好,就不要看完我在这瞎bb了,直接看原注释就行,比我说的好。)首先查看了前一个node的节点状态:
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
tryLock()
tryLock(long,TimeUnit )
解锁
流程图
-unlock()
注释:
SIGNAL(-1) :线程的后继线程正/已被阻塞,当该线程release或cancel时要重新这个后继线程(unpark)
CANCELLED(1):因为超时或中断,该线程已经被取消
CONDITION(-2):表明该线程被处于条件队列,就是因为调用了Condition.await而被阻塞
PROPAGATE(-3):传播共享锁
0:0代表无状态
如果你看完了,还不知道我再说什么,看下面这个帖子
http://www.cnblogs.com/MichaelPeng/archive/2010/02/12/1667947.html