重入锁ReentranLock,它表示该锁能够支持一个线程对资源的重复加锁。除此之外,该锁还支持获取锁时的公平性和非公平性选择。
一、属性变量
private final Sync sync;
定义在ReentranLock中的一个类,该类实现了AQS。并且有两个子类,分别是公平锁和非公平锁。根据需要可以创建不同的锁。
二、内部类
1、Sync类
abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L; abstract void lock(); //非公平锁进行获取同步状态 final boolean nonfairTryAcquire(int acquires) { } //释放同步状态 protected final boolean tryRelease(int releases) { } //锁是否被当前线程独占 protected final boolean isHeldExclusively() { } //监视器 final ConditionObject newCondition() { return new ConditionObject(); } }
2、NonfairSync类
static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; //非公平的获取同步状态 final void lock() { } //非公平的释放同步状态 protected final boolean tryAcquire(int acquires) { } }顾名思义,这是非公平锁的具体实现
3、FairSync类
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; //公平的获取同步状态 final void lock() { } //公平的释放同步状态 protected final boolean tryAcquire(int acquires) { } }
OK,这个就是公平锁的具体实现
三、非公平锁获取与释放同步状态
1、非公平获取锁
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(); } final boolean nonfairTryAcquire(int acquires) { //获取当前线程 final Thread current = Thread.currentThread(); //获取当前状态 int c = getState(); //如果当前状态等于0,说明可以获取同步状态 if (c == 0) { //如果成功获取同步状态,则将该锁设置为当前线程独占 if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } //如果锁状态不为0,说明有线程获取了该锁,这个线程可能是当前线程,也可能是其他线程, // 这里判断获取锁的线程是否为当前线程 //如果是当前线程,说明重入 else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); //重入次数加1 setState(nextc); return true; } //没有锁可获取并且获取锁的线程不是当前线程 return false; }
非公平的获取锁的步骤大致如下:
当前线程快锁的获取锁,获取失败则调用acquire方法去获取锁,acquire方法中先调用nonfairTryAcquire方法来获取锁。
tryfairTryAcquire方法先判断当前锁是否可获取,如果可获取则获取锁,不然判断当前线程是否要重入锁,如果是则重入。不然获取失败则构造同步节点加入到同步队列。
2、公平锁的获取锁
final void lock() { acquire(1); } protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); 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; } public final boolean hasQueuedPredecessors() { Node t = tail; Node h = head; Node s; //判断当前节点是否有前驱节点存在 return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
非公平锁和公平锁代码比较,唯一不同的地方为判断条件多个hasQueuedPredecessors方法,这个方法用来判断同步队列中当前节点是否有前驱节点,如果方法返回true,则表示有线程比当前线程更早地请求获取锁,因此要等待前驱线程获取锁并释放锁后才能继续获取锁。
3、锁的释放
public void unlock() { sync.release(1); } public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; } //如果tryRelease返回true,则唤醒后继线程 protected final boolean tryRelease(int releases) { int c = getState() - releases; //如果当前线程不是占有锁的线程,则抛出异常(也就是释放锁时必须获取锁) if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; //如果释放成功,将独占锁的线程清空 if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; }
在最后说一下,没有搞懂AQS之前,先去看AQS,看懂这些实现都so easy,不然很头疼。