ReentrantLock 一个可重入的互斥锁 Lock
,它具有与使用 synchronized
方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。 这个类主要基于AQS(AbstractOwnableSynchronizer)封装的 公平与非公平锁。
所谓公平锁就是指 在多个线程的争用下,这些锁倾向于将访问权授予等待时间最长的线程,换句话说也就是先被锁定的线程首先获得锁。 非公平锁正好相反,解锁时没有固定顺序。
让我们边分析源代码边学习如何使用该类
先来看一下构造参数,默认是非公平锁。
1 /**
2 * Creates an instance of {@code ReentrantLock}. 3 * This is equivalent to using {@code ReentrantLock(false)}. 4 */
5 public ReentrantLock() { 6 sync = new NonfairSync(); 7 } 8
9 /**
10 * Creates an instance of {@code ReentrantLock} with the 11 * given fairness policy. 12 * 13 * @param fair {@code true} if this lock should use a fair ordering policy 14 */
15 public ReentrantLock(boolean fair) { 16 sync = fair ? new FairSync() : new NonfairSync(); 17 }
NonfairSync是非公平锁,我们先来看非公平锁,是一个内部类继承了Sync。
1 /**
2 * Sync object for non-fair locks 3 */
4 static final class NonfairSync extends Sync { 5 private static final long serialVersionUID = 7316153563782823691L; 6
7 /**
8 * Performs lock. Try immediate barge, backing up to normal 9 * acquire on failure. 10 */
11 final void lock() { 12 if (compareAndSetState(0, 1)) 13 setExclusiveOwnerThread(Thread.currentThread()); 14 else
15 acquire(1); 16 } 17
18 protected final boolean tryAcquire(int acquires) { 19 return nonfairTryAcquire(acquires); 20 } 21 }
我们看到它继承了Sync,我们接着看这个类的源码。
1 abstract static class Sync extends AbstractQueuedSynchronizer { 2 private static final long serialVersionUID = -5179523762034025860L; 3
4 abstract void lock(); 5
6 final boolean nonfairTryAcquire(int acquires) { 7 final Thread current = Thread.currentThread(); 8 int c = getState(); 9 if (c == 0) { 10 if (compareAndSetState(0, acquires)) { 11 setExclusiveOwnerThread(current); 12 return true; 13 } 14 } 15 else if (current == getExclusiveOwnerThread()) { 16 int nextc = c + acquires; 17 if (nextc < 0) // overflow
18 throw new Error("Maximum lock count exceeded"); 19 setState(nextc); 20 return true; 21 } 22 return false; 23 } 24
25 protected final boolean tryRelease(int releases) { 26 int c = getState() - releases; 27 if (Thread.currentThread() != getExclusiveOwnerThread()) 28 throw new IllegalMonitorStateException(); 29 boolean free = false; 30 if (c == 0) { 31 free = true; 32 setExclusiveOwnerThread(null); 33 } 34 setState(c); 35 return free; 36 } 37 }
Sync这个类与AbstractQueuedSynchronizer 一起完成了锁的逻辑,现在我们开始从头分析一个线程如何获取锁,以及获取不到锁时如何被阻塞。当用户调用lock方法获取锁的时候,首先会先通过compareAndSetState(NonfairSync第11行)来设置锁定状态,如果原先状态为0,则说明目前没有线程持有锁,那么设置状态为1,并且设置当前线程是当前拥有独占访问的线程(setExclusiveOwnerThread),那么另外一种情况就是compareAndSetState方法返回false,也就是说之前已经有线程持有锁,那么就会执行acquire方法(NonfairSync第15行),这个方法是AbstractQueuedSynchronizer里面的方法。
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
首先调用了tryAcquire方法,这个类在子类当中实现,也就是上面NonfairSync第18行,实际上他调用了 nonfairTryAcquire,这个方法分两步,首先在判断一下状态(state)是否等于0,也就是重新尝试获取所,如果获取到锁则改变状态compareAndSetState 然后设置当前线程是当前拥有独占访问的线程(setExclusiveOwnerThread),跟上面讲到的一样。
如果重新尝试获取所失败,则判断是不是当前线程重复加锁,如果是的话就把状态进行增加。
如果上面都不是就返回FALSE, 如果返回FALSE 那么 acquire(int arg)方法的acquireQueued就会执行,这个方法会把不能获取锁的线程形成一个CHL队列保存起来,然后把线程阻塞。上面就基本讲完了
线程如何获取锁, 获取到锁就把状态设置成1。
如果是持有锁的线程继续调用 LOCK方法,那就把状态进行叠加。
如果获取不到锁,那么AbstractQueuedSynchronizer 会把线程以一个CHL队列的形式保存起来,然后设置线程阻塞,等待释放。
然后就是释放锁的操作
1 public void unlock() { 2 sync.release(1); 3 }
这个release方法又是AbstractQueuedSynchronizer 里面提供的方法。
1 public final boolean release(int arg) { 2 if (tryRelease(arg)) { 3 Node h = head; 4 if (h != null && h.waitStatus != 0) 5 unparkSuccessor(h); 6 return true; 7 } 8 return false; 9 }
首先在第2行先调用tryRelease尝试释放锁,这个方法是在ReentrantLock的内部类Sync当中重写的,释放成功会返回TRUE。然后调用unparkSuccessor来释放阻塞队列当中的线程,然后被唤醒的线程会继续获取锁,如此反复。
最后我们再来看一下说明是公平锁FairSync。
1 static final class FairSync extends Sync { 2 private static final long serialVersionUID = -3000897897090466540L; 3
4 final void lock() { 5 acquire(1); 6 } 7
8 /**
9 * Fair version of tryAcquire. Don't grant access unless 10 * recursive call or no waiters or is first. 11 */
12 protected final boolean tryAcquire(int acquires) { 13 final Thread current = Thread.currentThread(); 14 int c = getState(); 15 if (c == 0) { 16 if (!hasQueuedPredecessors() &&
17 compareAndSetState(0, acquires)) { 18 setExclusiveOwnerThread(current); 19 return true; 20 } 21 } 22 else if (current == getExclusiveOwnerThread()) { 23 int nextc = c + acquires; 24 if (nextc < 0) 25 throw new Error("Maximum lock count exceeded"); 26 setState(nextc); 27 return true; 28 } 29 return false; 30 } 31 }
仔细观察你会发现它与非公平锁唯一的区别就是在tryAcquire这里方法里面。上面已经用红色标记上了,主要是多了这么一个条件,就体现了锁的公平性。
当一个线程尝试获取锁时,那么先会判断当前有没有已经等待获取锁的线程队列,如果的话,按照公平原则,那么当前线程就会被加入阻塞队列的尾巴,如果是非公平锁,那么则不会判断。
这个类的核心原理基本介绍完了,其实主要核心的东西是在AbstractQueuedSynchronizer这个类里。
这里给大家提供一个关于AbstractQueuedSynchronizer的源码分析的文章:
http://ifeve.com/introduce-abstractqueuedsynchronizer/ 大家可以结合着一起看一下。