ReentrantLock 是一种可重入的互斥锁,它不像 synchronized关键字一样支持隐式的重进入,但能够使一个线程(不同的方法)重复对资源的重复加锁而不受阻塞。
ReentrantLock 的 Java类图:
其中抽象静态内部类 Sync 继承了 AQS,可见 ReentrantLock 是通过组合自定义同步器来实现锁的获取与释放。
ReentrantLock 的构造函数能控制锁的公平性表现:
1 public ReentrantLock(boolean fair) { 2 sync = fair ? new FairSync() : new NonfairSync(); 3 }
公平锁的够减少“饥饿”发生概率,等待越久的请求越能够优先的到满足。
公平性与否是针对获取锁而言的,一个锁若是公平的,那么锁的获取顺序就应该符合请求的绝对时间顺序,先到先得。
公平性锁每次都是从同步队列中的第一个节点获取到锁,而非公平性锁会出现一个线程连续获取锁的情况,特别是刚释放锁的线程在次获取同步状态的几率会更大,使得其他线程在同步队列中等待。
在系统线程上下文切换方面上,非公平性锁的切换次数会明显少于公平性锁,因为公平性锁的切换开销相对更大,但是极少的线程切换能保证更大吞吐量。
ReentrantLock 类代码的基本结构:
1 public class ReentrantLock implements Lock, java.io.Serializable { 2 private final Sync sync; 3 4 //默认无参构造函数,默认为非公平锁 5 public ReentrantLock() { 6 sync = new NonfairSync(); 7 } 8 9 //带参数的构造函数,决定是公平锁还是非公平锁 10 public ReentrantLock(boolean fair) { 11 sync = fair ? new FairSync() : new NonfairSync(); 12 } 13 14 //抽象基类继承AQS,公平锁与非公平锁继承该类,并分别实现其lock()方法 15 abstract static class Sync extends AbstractQueuedSynchronizer { 16 abstract void lock(); 17 //省略.. 18 } 19 20 //非公平锁实现 21 static final class NonfairSync extends Sync {...} 22 23 //公平锁实现 24 static final class FairSync extends Sync {....} 25 26 //锁实现,根据具体子类实现调用 27 public void lock() { 28 sync.lock(); 29 } 30 31 //响应中断的获取锁 32 public void lockInterruptibly() throws InterruptedException { 33 sync.acquireInterruptibly(1); 34 } 35 36 //尝试获取锁,默认采用非公平锁方法实现 37 public boolean tryLock() { 38 return sync.nonfairTryAcquire(1); 39 } 40 41 //超时获取锁 42 public boolean tryLock(long timeout, TimeUnit unit) 43 throws InterruptedException { 44 return sync.tryAcquireNanos(1, unit.toNanos(timeout)); 45 } 46 47 //释放锁 48 public void unlock() { 49 sync.release(1); 50 } 51 52 //创建锁条件(从Condetion来理解,就是创建等待队列) 53 public Condition newCondition() { 54 return sync.newCondition(); 55 } 56 57 //省略.... 58 }
ReentrantLock 释放锁的方法,在抽象静态内部类 Sync 中,它会检查当前线程同步状态值(重复获取一次锁,状态值会增加),在释放同步状态时减少同步状态值,若该锁获取了 n 次,那么前(n-1)次返回的都是 false,只有同步状态完全释放了(c为0),才会把占有该资源的线程设置为空,并返回 true。
1 protected final boolean tryRelease(int releases) { 2 int c = getState() - releases; 3 if (Thread.currentThread() != getExclusiveOwnerThread()) 4 throw new IllegalMonitorStateException(); 5 boolean free = false; 6 if (c == 0) { 7 free = true; 8 setExclusiveOwnerThread(null); 9 } 10 setState(c); 11 return free; 12 }