JUC-ReentrantLock源码分析
1. ReentrantLock定义
PS:要想理解ReentrantLock原理,需要先了解AQS,不了解AQS的可以看先之前的文章->aqs源码解析
ReentrantLock是jdk提供的可中断, 可重入获取, 支持超时, 支持尝试获取锁。它与synchronized锁主要有以下几点不同之处:
- 可重入, 一个线程获取独占锁后, 可多次获取, 多次释放(synchronized也一样, 只是synchronized内的代码执行异常后会自动释放到monitor上的锁)
- 支持中断(synchronized不支持)
- 支持超时机制, 支持尝试获取lock, 支持公不公平获取lock(主要区别在 判断 AQS 中的 Sync Queue 里面是否有其他线程等待获取 lock)
- 支持调用 Condition 提供的 await(释放lock, 并等待), signal(将线程节点从 Condition Queue 转移到 Sync Queue 里面)
- 在运行 synchronized 里面的代码若抛出异常, 则会自动释放监视器上的lock, 而 ReentrantLock 是需要显示的调用 unlock方法
下面我们先来看看reentrantLock如何使用的:
public class ReentrantLockDemo extends Thread { public static Lock lock = new ReentrantLock(); public static int i = 0; public ReentrantLockDemo(String name) { super(name); } public static void main(String[] args) { new ReentrantLockDemo("thread1").start(); new ReentrantLockDemo("thread2").start(); new ReentrantLockDemo("thread3").start(); } @Override public void run(){ try { lock.lock(); System.out.println(Thread.currentThread().getName() + "获取lock锁"); TimeUnit.SECONDS.sleep(1); }catch (Exception e){ e.printStackTrace(); }finally { // 因为程序发生异常,lock锁不会自动释放,所以放在finally中释放锁 lock.unlock(); } } }
同synchronized一样,对于同一把锁,在同一时刻只会有一个线程获取到锁
2. ReentrantLock特性
2.1 公平锁和非公平锁
public ReentrantLock() { sync = new NonfairSync(); } /** * Creates an instance of {@code ReentrantLock} with the * given fairness policy. * * @param fair {@code true} if this lock should use a fair ordering policy */ public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
ReentrantLock内部提供两种锁实现,分别是FairSync(公平锁)和NonfairSync(非公平锁)。通过上面的构造方法可以看出,默认是非公平锁,这样的好处是吞吐量高。可以通过有参构造传值,决定使用公平锁或者非公平锁。
2.2 重入锁
和Synchronized一样,ReentrantLock也支持重入锁。因为ReentrantLock是基于AQS实现的(了解aqs可以看我之前的文章,aqs源码解析),当获取锁的线程再次尝试获取锁的时候,通过state++标记锁的获取次数。
3. 源码分析
公平锁和非公平锁的父类:
abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L; /** * Performs {@link Lock#lock}. The main reason for subclassing * is to allow fast path for nonfair version. */ abstract void lock(); /** * Performs non-fair tryLock. tryAcquire is implemented in * subclasses, but both need nonfair try for trylock method. */ 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; } 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; } 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(); } final ConditionObject newCondition() { return new ConditionObject(); } // Methods relayed from outer class final Thread getOwner() { return getState() == 0 ? null : getExclusiveOwnerThread(); } final int getHoldCount() { return isHeldExclusively() ? getState() : 0; } final boolean isLocked() { return getState() != 0; } /** * Reconstitutes the instance from a stream (that is, deserializes it). */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); setState(0); // reset to unlocked state } }
公平锁:
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } /** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. */ 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; } }
非公平锁:
static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; /** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }
3.1 lock()分析
我们可以看到FairSync和NoFairSync都继承自Sync,而Sync继承自AbstractQueuedSynchronizer,下面我们来分析获取锁的过程:
final void lock() { acquire(1); }
当调用lock方法时,实际调用的是AQS中的acquire方法
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
AQS中的tryAcquire方法是抽象方法,具体的实现是在FairSync和NoFairSync中的,下面是Sync中tryAcquire的具体实现:
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; }
当tryAcquire方法调用后,会继续走aqs的逻辑去尝试获取锁,如果没有获取到锁,会加入到aqs队列中。
3.2 unlock()分析
public void unlock() { sync.release(1); }
当调用unlock方法时,调用的也是aqs的release方法:
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方法同上,在aqs中也是抽象方法,具体实现也是在Sync中:
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逻辑进行释放锁操作。
3.3 公平锁和非公平锁分析
3.3.1 非公平锁FairSync
非公平锁中的lock逻辑:
final void lock() { // 会直接尝试获取锁 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
当线程调用lock方法时,不管aqs中等待队列中是否有线程等待,会直接调用compareAndSetState方法尝试获取锁,所以获取锁操作时非公平的。
3.3.2 公平锁NoFairSync
公平锁中的lock逻辑:
final void lock() { acquire(1); }
当线程调用lock方法时,直接走aqs的acquire逻辑,判断队列中是否有线程等待,如果有线程等待,不会尝试获取锁,而是加入到等待队列的尾部,服从先到先得的逻辑。
- JUC - ReentrantLock 源码分析
- JUC源码分析13-locks-ReentrantReadWriteLock
- JUC源码分析9-locks-ReentrantLock
- 【JUC】JDK1.8源码分析之ReentrantLock(三)
- 【JUC】JDK1.8源码分析之ReentrantLock
- JUC--ReentrantLock及Condition源码分析
- JUC - ReentrantReadWriteLock 源码分析
- 【Java8源码分析】locks包-ReentrantReadWriteLock
- 【JUC源码解析】ReentrantReadWriteLock
- Java显式锁学习总结之四:ReentrantLock源码分析
- ReentrantLock 源码分析
- jdk 源码分析(9)java ReentrantReadWriteLock分析
- Java显式锁学习总结之四:ReentrantLock源码分析
- JUC源码分析5-locks-LockSupport
- Java并发编程 ReentrantLock 源码分析
- 并发编程(三)—— ReentrantLock实现原理及源码分析
- Java并发编程之ReentrantLock源码分析
- ReentrantLock源码分析
- Java显式锁学习总结之四:ReentrantLock源码分析
- 【JDK源码分析】通过源码彻底理解ReentrantLock显示锁