ReentrantLock是显示锁,Synchronized叫内置锁是JDK1.6中由道格李引入的。他们的关系就像 wait/notify 和 condition。RenentrantLock的实现和CountDownLatch的实现基本相同,唯一的区别是ReentrantLock有两种实现,分别对应公平锁和非公平锁。
非公平锁实现源码分析
ReentrantLock默认提供的是非公平锁的实现,在默认的构造方法中实例化了ReentrantLock的实例,此时AQS的同步状态被赋初始值0,表示现在锁是可以获取的。
构造方法
1 2 3
| public ReentrantLock() { sync = new NonfairSync(); }
|
非公平锁获取源码分析
lock方法在Sync类中被定义为抽象方法,具体的实现由公平锁和非公平锁去实现。
1 2 3 4 5 6 7 8 9 10
| 锁的获取 final void lock() { 不管锁是否被获取先CAS尝试获取锁 if (compareAndSetState(0, 1)) 如果获取到锁之后,就设置当前线程表示当前线程获取到了锁 setExclusiveOwnerThread(Thread.currentThread()); else 如果CAS失败表示锁已经被其他的线程获取到了,就构建等待队列,将当前线程添加到队尾 acquire(1); }
|
acquire的具体实现如下:
1 2 3 4 5 6 7
| public final void acquire(int arg) { if (!tryAcquire(arg) && //先CAS尝试获取一次锁 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)){//addWaiter就是以当前线程构造节点添加到等待队列,acquireQuqued中会再次尝试获取一次锁,注意获取失败才返回false 两次获取失败 就将当前线程挂起 selfInterrupt(); } }
|
NonfairSync的tryAcquire实现如下:
1 2 3
| protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 非公平的方式获取一次锁 final boolean nonfairTryAcquire(int acquires) { 获取当前线程 final Thread current = Thread.currentThread(); int c = getState(); 如果状态为0表示锁是可以获取的,此时不管有没有等待队列,直接进行CAS操作尝试获取到锁 if (c == 0) { CAS获取到锁,如果获取成功就,返回true并设置当前线程获取到锁 if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { 如果状态不为0表示锁已经被获取到了,就判断锁是否被当前线程获取到了(可重入锁),如果是当前线程获取到了锁,就将锁计数加一 int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
|
acquireQueued方法是AQS的典型实现,在AQS源码分析文章中已经讲过。作用是将线程添加到等待队列。
注意:这里返回true表示,获取当前线程再次尝试获取锁失败。或者可以任务将当前线程添加到等待队列成功。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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; } 这里会将Node节点的状态由默认的0更新为-1 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
|
非公平锁释放源码分析
释放锁调用的是AQS类的release方法
1 2 3
| public void unlock() { sync.release(1); }
|
1 2 3 4 5 6 7 8 9 10
| public final boolean release(int arg) { //首先调用子类的release方法,如果成功,就激活等待锁的其他的节点,然后返回。 if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
|
我们来看Lock类是如何实现tryRelease方法的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| protected final boolean tryRelease(int releases) { //首先将同步状态减一 int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; //如果不是可重入锁,这里状态就会为0,然后返回即可 if (c == 0) { free = true; setExclusiveOwnerThread(null); } //如果是可重入锁 就CAS将锁的同步状态减一。 setState(c); return free; }
|
公平锁源码分析
公平锁构造函数
根据传入的参数,构造公平锁和非公平锁
1 2 3
| public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
|
公平锁获取源码分析
公平锁的获取和非公平锁的获取的区别是:
- 非公平锁,不会判断是否有等待队列尝试获取锁,如果失败,会加入到等待队列的末尾
- 公平锁,会判断是否有等待队列,如果有,就会将自己加入等待队列
以下两个步骤和非公平锁一样调用的都是AQS的方法。1 2 3
| final void lock() { acquire(1); }
|
1 2 3 4 5 6
| 获取同步状态 public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
|
公平锁和非公平锁的唯一区别是tryAcquire方法。方法的具体实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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; }
|
公平锁释放源码分析
公平锁的释放和非公平锁的释放过程,完全一样。