ReentrantLock与synchronized都是可重入锁,都是独占锁。
可重入锁的意思就是在一个已获得锁的线程中,可以再次获得同一把锁。
区别在于:
1、ReentrantLock可以设置锁等待的超时时间(避免一致等待下去)
2、ReentrantLock可以实现公平锁与非公平锁(阻塞线程是否按照先来先获得锁的顺序)
3、ReentrantLock可以设置条件(Condition)
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();
}
// Sync是一个抽象类,继承了AQS
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
// 抽象lock方法,由两个子类:公平锁和非公平锁实现
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
// 执行非公平的tryLock(),tryAcquire()在子类中实现,
// 但是两个子类对tryLock()需要非公平的去获取
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取state的值。此值是AQS里面的成员变量,0代表此时没有线程拥有锁
int c = getState();
// 如果没有线程拥有锁,则当前线程通过CAS原子方式把state设置为acquires(acquires传入是1)
// 然后把当前线程设置为独占这把锁的线程
// 最后返回true表示获取锁成功
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 如果当前线程是这把锁的独占的主人,也就是当前线程已经获得这把锁了,现在再次来获取这把锁
// 这就是可重入锁的意思,把state+1设置一下,
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
// 一直+++加到溢出了...
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 因为当前线程是在已经获取这把锁的情况下再来获取锁,所以不需要以CAS方式设置state(独占状态)
setState(nextc);
return true;
}
// 如果在c==0的时候获取失败了,现在就返回false
return false;
}
// 尝试释放锁
protected final boolean tryRelease(int releases) {
// 释放c的值
int c = getState() - releases;
// 如果当前线程不是这把锁当前的独占线程,就抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 考虑到重入锁的情况,c可能不等于0
// 如果c==0,那么锁释放了,设置独占这把锁的线程为null
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
// 不管有没有把c释放干净,都是释放了releases个,设置c的值
setState(c);
// 如果c==0就返回true,否则返回false
return free;
}
// 判断当前线程是否独占了锁
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
// 新建一个ConditionObject
final ConditionObject newCondition() {
return new ConditionObject();
}
// 获取当前获取锁的线程
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
// 获取这把锁已经被一个线程重入的次数
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
// 是否锁了
final boolean isLocked() {
return getState() != 0;
}
// 序列化,重置state值
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
// Sync儿子:非公平锁
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
// 获取锁,若失败就执行acquire(AQS)
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 下文看它的实现过程,会先执行下面的tryAcquire
acquire(1);
}
// 以非公平的方式尝试获取锁,哪个线程先抢到哪个就获得锁
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
// Sync儿子:公平锁
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
// 尝试获取锁
final void lock() {
acquire(1);
}
// 公平锁版本的tryAcquire
protected final boolean tryAcquire(int acquires) {
// 当前线程
final Thread current = Thread.currentThread();
int c = getState();
// c==0,锁处于可获取状态
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;
}
}
// 现在肯定迫不及待的想看AQS里面的一些实现,但是还是先忍一忍,看下ReentrantLock里面的其他方法
// 毕竟看到上面的爸爸Sync已经也实现了那么多方法,肯定是ReentrantLock调用了
// 再啰嗦一句就是现在的sync是公平锁或者非公平锁其中一个,看下ReentantLock的构造方法
// lock方法就是调用公平锁或者非公平锁的其中一个
public void lock() {
sync.lock();
}
// 锁中断
public void lockInterruptibly() throws InterruptedException {
// 调用的是AQS的方法
sync.acquireInterruptibly(1);
}
// tryLock是调用非公平锁的nonfairTryAcquire()
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
// 带有超时的tryLock
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
// AQS的方法
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
// 释放锁
public void unlock() {
sync.release(1);
}
}
// 接下来正式看下AQS,看的过程还是要结合上面的ReentrantLock来看
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
// 直接看获取锁
public final void acquire(int arg) {
// tryAcquire是由子类重写的,公平锁与非公平锁是有没有判断当前线程在队列的位置
// 如果tryAcquire返回true,代表获取到锁了,这个方法结束
// 如果tryAcquire返回false,代表获取锁失败了,执行addWaiter和acquireQueued方法
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// 把节点放到等待队列的尾节点
// mode从上面传入来看是null
// 返回放入队列的节点
private Node addWaiter(Node 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;
// 如果set失败,尾节点这时候还没有任何变动
if (compareAndSetTail(pred, node)) {
// 尾节点与新节点链接起来,新节点变尾节点
pred.next = node;
return node;
}
}
// 尾节点为null,第一次入队
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 {
// 已经有尾节点了,但是刚刚在addWaiter那CAS失败了,在这里接着CAS设置尾节点为node
// 如果是第一次,那这个t也可能是上面的new Node()
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
// 输入尾节点和1
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 获取node的前一个节点(必定存在)
final Node p = node.predecessor();
// 如果p是头节点并且能够获得锁成功
// 那就设置头节点为node,释放头节点资源,返回中断
// 这里其实就是要一直在循环里等node的前一个获得锁,然后下一个是node获得锁(这就是FIFO)
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)
cancelAcquire(node);
}
}
// 检查和更新获取锁失败的节点,如果线程应该被阻塞旧返回true
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 获取前一个节点的状态
int ws = pred.waitStatus;
// 如果前一个节点处于SIGNAL状态,那么就是要唤醒他的后继节点
if (ws == Node.SIGNAL)//SIGNAL=-1
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
// 前节点处于取消状态,就去找前面的有效节点
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
// ws > 0代表节点已经被删除了,需要循环判断去取前节点的前节点
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
// 找到的前前前节点与node链接
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
// 设置当前线程的waitstatus为SIGNAL
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
private final boolean parkAndCheckInterrupt() {
// 阻塞当前线程,等待唤醒
LockSupport.park(this);
// 返回线程的中断状态并清除中断标志
return Thread.interrupted();
}
// 释放锁
public final boolean release(int arg) {
// 先尝试释放锁
if (tryRelease(arg)) {
Node h = head;
// 判断头结点不为null并且头结点的状态不为0也即有效状态
if (h != null && h.waitStatus != 0)
// 唤醒头结点(此时的头结点是释放之前的排在队列第一位和之前的头结点相链接的节点)
unparkSuccessor(h);
return true;
}
return false;
}
// Sync里面实现了tryRelease
protected final boolean tryRelease(int releases) {
// 计算释放后state的值
int c = getState() - releases;
// 释放的时候,当前线程不是正在执行的线程就抛异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 如果c==0,说明锁释放干净了
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
// 设置一下state
setState(c);
return free;
}
// 唤醒后继节点
private void unparkSuccessor(Node node) {
// 清除当前线程的waitstatus
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
// 获取当前节点的后继节点
Node s = node.next;
// 如果后继节点处于被取消或者是null的状态,从尾节点回溯查找没有被取消的节点
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// 如果s不为null,唤醒后继节点对应的线程
if (s != null)
LockSupport.unpark(s.thread);
}