ReentrantLock:
1.源码层面分析:
public class ReentrantLock implements Lock, java.io.Serializable { private static final long serialVersionUID = 7373984872572414699L; //实现同步逻辑的同步器,提供了所有锁相关的操作 private final Sync sync; //默认构造函数,提供了非公平锁 public ReentrantLock() { sync = new NonfairSync(); } /** * 根据策略(公平,非公平)来构建重入锁 * @param fair true表示采用公平锁,false表示采用非公平锁 */ public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } //获取锁,获取成功则返回,否则阻塞获取线程直到获取成功 public void lock() { sync.lock(); } //获取锁,获取成功则返回,否则阻塞获取线程直到获取成功;并响应中断,也就是说获取线程被Thread.interrupt()中断后,该方法会抛出InterruptedException并返回。 public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } /** * 尝试获取锁,不管成功,失败都立即返回,不会阻塞 * @return true表示获取成功,false表示失败 */ public boolean tryLock() { return sync.nonfairTryAcquire(1); } /** * 尝试获取锁,获取成功则返回,否则等待timeout时间返回 * @param timeout 等待获取锁的超时时间 * @param unit 时间单位 * @return true表示获取成功,false表示超时时间到达仍然获取失败 */ public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); } //释放锁,该方法不会阻塞 public void unlock() { sync.release(1); } /** * 获取一个条件对象 * @return 条件对象 */ public Condition newCondition() { return sync.newCondition(); }
2.Sync内部类:
//同步抽象类,基于AQS实现 abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L; //具体由子类NoFairSync,FairSync实现 abstract void lock(); /** * 非公平的方式尝试获取资源 * @param acquires 资源个数 * @return true表示获取成功,false获取失败 */ final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); //获取资源状态,volatile读 int c = getState(); //0表示初始状态或者锁释放状态,可以直接获取资源 if (c == 0) { //CAS判断资源获取是否成功 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; } /** * 尝试释放资源 * @param releases 资源个数 * @return true表示释放成功,false释放失败 */ 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; //设置资源的独占线程为null,此时没有线程占用资源 setExclusiveOwnerThread(null); } setState(c); return free; } /** * 判断当前线程是否持有资源 * @return true表示是,false表示否 */ protected final boolean isHeldExclusively() { return getExclusiveOwnerThread() == Thread.currentThread(); } /** * 获取条件对象 * @return 新的条件对象 */ final ConditionObject newCondition() { return new ConditionObject(); } /** * 获取持有资源的线程 * @return 持有资源的线程 */ final Thread getOwner() { return getState() == 0 ? null : getExclusiveOwnerThread(); } /** * 获取持有资源的个数 * @return 持有资源的个数 */ final int getHoldCount() { return isHeldExclusively() ? getState() : 0; } /** * 获取资源是否被线程占用 * @return true表示是,false否 */ final boolean isLocked() { return getState() != 0; } }
ReentrantLock内部基于Sync提供了非公平锁和公平锁的实现。
3.非公平锁
//非公平锁继承自Sync static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; /** * 非公平锁提供的锁定方法 */ final void lock() { //先尝试CAS获取操作,获取成功则设置当前线程为资源持有线程并返回 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else //获取失败则进入AQS的acquire逻辑,排队获取资源 acquire(1); } /** * 非公平的方法尝试获取资源,AQS对外提供的需要实现方法 * @param acquires资源个数 * @return true表示是,false否 */ protected final boolean tryAcquire(int acquires) { //用Sync类的非公平方式尝试获取资源 return nonfairTryAcquire(acquires); } }
4.公平锁:
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; /** * 公平锁提供的锁定方法 */ final void lock() { //排队获取资源,体现公平性 acquire(1); } /** * 公平的方式尝试获取资源 * @param acquires资源个数 * @return true表示是,false否 */ protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); //0表示当前可以直接获取资源 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; } }
5.**总结:**非公平主要体现在资源获取时,如果获取线程发现当前资源可以获取成功(其他线程释放了资源)那就直接获取返回,而此时可能有其他线程先于当前线程在排队获取,那么对于这部分线程来说是“不公平”的,没有体现先到先得的原则。反之,就是公平锁,先排队的线程先获取锁,不存在插队的情况,体现了公平性,所以叫公平锁。
6.ReentrantLock默认使用非公平方式提供锁的获取和释放,可以看到相比公平模式,非公平模式获取锁的效率更高,因为在非公平模式下如果能够获取锁成功则线程不需要再排队,而在公平模式下,所有线程都必须排队获取锁,所以从当前获取锁的线程的角度非公平模式效率更高。还有一个地方需要说明的是:ReentrantLock是可重入锁,也就是说同一个线程获取锁后可再次重复获取锁无需阻塞。
7.典型应用场景:
class X { private final ReentrantLock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public void m() { lock.lock(); // 获取锁 try { try { //必须先获取锁才能执行条件等待,其他线程需要执行condition.signal()方法,来唤醒等待线程 condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } // 执行具体业务方法 } finally { lock.unlock() //释放锁,必须在finally块中,防止发生异常时未执行释放锁操作,导致其他线程无法获取锁 } } }}