我的jdk源码(二十三):ReentrantLock类
一、概述
ReentrantLock类是在内部利用自己的内部类Sync继承了AbstractQueuedSynchronizer,实现了tryAcquire()方法,在这个方法中增强自己的功能,比如添加了重入和公平锁、非公平锁。ReentrantLock再将Sync作进一步的封装,开放出去Lock的接口。
ReentrantLock是通过在请求锁的时候判断CLH列表有没有比当前线程等待时间更久的线程来实现公平性的。当一个线程请求公平锁的时候,如果state为0,还需要在CHL中没有等待节点,或CHL首位就是当前线程,那么就可以使用CAS请求锁。否则请求直接失败。非公平锁没有这个限制,只要state为0,就可以使用CAS操作请求锁。非公平锁的吞吐量要比公平锁要高,因为公平锁就是完全要入队然后依次执行,在入队后线程会被park,到当前线程后需要unpark。线程之前的切换,资源准备等都是比较耗时的。非公平锁在在任务请求获取锁的时候如果正好锁被释放,则可以不用入队省去了线程阻塞唤醒。
二、源码分析
1. 类的声明
public class ReentrantLock implements Lock, java.io.Serializable
ReentrantLock类实现了Lock和Serializable接口,表示其实例是可序列化的琐。
2. 构造函数
// 构造一个 ReentrantLock 实例(非公平锁) public ReentrantLock() { sync = new NonfairSync(); } // 构造一个 ReentrantLock 实例(指定是否公平) public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
3. 成员变量
//序列化标识id private static final long serialVersionUID = 7373984872572414699L; //同步器实例,可以是公平琐,也可以是非公平琐 private final Sync sync;
4. 内部类-Sync类
//Sync也是一个抽象类,因为锁有非公平和公平的区别。 abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L; //非公平和公平锁的lock()方法有不同的实现。 abstract void lock(); //tryLock()在子类中实现,该方法是非公平的独占式获取同步状态。 //该方法首先判断同步状态是否被获取,如果没有,CAS获取同步状态并将锁的拥有者设为当前线程,如果有,判断获取锁的线程是否是当前线程,如果是,将同步值进行累加。 //成功获取锁的线程,再次获取锁,只是增加了同步值。 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; } //获取了多少次锁,同样也要释放多少次锁。 //当同步值不为0时,还是当前线程占有锁。 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() { return getExclusiveOwnerThread() == Thread.currentThread(); } final ConditionObject newCondition() { return new ConditionObject(); } //得到锁的占有者 final Thread getOwner() { return getState() == 0 ? null : getExclusiveOwnerThread(); } //获取锁的次数 final int getHoldCount() { return isHeldExclusively() ? getState() : 0; } //锁是否被获取 final boolean isLocked() { return getState() != 0; } //反序列化操作 private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); setState(0); // reset to unlocked state } }
ReentrantLock通过将继承AQS的子类sync作为类成员变量来实现锁,sync实现AQS的抽象方法来管理同步状态。锁的获取和释放是通过修改 AQS 的 state 变量来实现的。lock 方法可以看做对 state 执行“加法”操作,而 unlock 可以看做对 state 执行“减法”操作,当 state 为 0 时,表示当前没有线程占用资源。
5. 内部类-NonfairSync类
//非公平锁的实现 static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; //非公平锁加锁,acquire调用tryAcquire,tryAcquire调用nonfairTryAcquire final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }
非公平锁的lock()方法步骤为:先尝试以 CAS 方式修改 state 的值,若修改成功,则表示成功获取到锁,将 owner 设为当前线程;否则就执行 AQS 中的 acquire 方法。
6. 内部类-FairSync类
//公平锁的实现 static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } //lock()调用acquire(),acquire()调用tryAcquire(),公平锁和非公平锁的tryAcquire()的实现唯一不同 //就是加入了hasQueuesPredecessors(),通过判断当前节点是否有前驱节点,如果有则当前节点不是等待时间最长的线程 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; } }
非公平锁相比,公平锁的不同之处在于增加了判断条件 hasQueuedPredecessors,即首先判断主队列中是否有其他线程在等待,当没有其他线程在排队时再去获取,否则获取失败。
公平锁和非公平锁的主要在于两个地方:
(1) 获取锁lock()方法的不同:非公平锁会直接进行一次CAS,如果CAS成功,就直接获取锁了。
(2) tryAcquire()方法实现的不同:当同步状态此时被释放的时候,state等于0,并没有任何线程占有锁,如果是公平锁,会判断队列中是否有线程等待获取锁,如果有的话,直接将自己构造成一个节点加入同步队列,但是非公平锁会直接CAS设置状态,不考虑同步队列中是否有其他线程等待获取锁。如果获取同步状态失败,会将自己构造成一个节点加入同步队列中,加入同步队列之后,等待前驱节点释放同步状态,唤醒自己,这后面的步骤公平锁和非公平锁是一样的。
7. ReentrantLock其他方法
//构造函数默认是非公平锁 public ReentrantLock() { sync = new NonfairSync(); } //可选择实现公平锁 public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } //获取锁 public void lock() { sync.lock(); } //可中断式的获取锁 public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } //尝试非阻塞的获取锁,调用方法之后立马返回,能获取返回true,不能获取返回false public boolean tryLock() { return sync.nonfairTryAcquire(1); } //释放锁 public void unlock() { sync.release(1); } //获取等待通知组件,该组件和当前锁绑定 public Condition newCondition() { return sync.newCondition(); } //锁被获取的次数 public int getHoldCount() { return sync.getHoldCount(); } //锁是否被当前线程所占有 public boolean isHeldByCurrentThread() { return sync.isHeldExclusively(); } //锁是否已经被获取 public boolean isLocked() { return sync.isLocked(); } //是否是公平锁 public final boolean isFair() { return sync instanceof FairSync; } //获取锁的所有者 protected Thread getOwner() { return sync.getOwner(); } //是否有线程等待该锁 public final boolean hasQueuedThreads() { return sync.hasQueuedThreads(); } //查询给定线程是否在等待该锁 public final boolean hasQueuedThread(Thread thread) { return sync.isQueued(thread); } //查询等待锁的线程数 public final int getQueueLength() { return sync.getQueueLength(); } //返回等待锁的线程集合 protected Collection<Thread> getQueuedThreads() { return sync.getQueuedThreads(); }
三、总结
ReentrantLock类总的来说,就是利用内部类sync类的子类NonfairSync类和FairSync类来实现非公平琐与公平琐,sync类则是继承自AbstractQueuedSynchronizer类。
ReentrantLock类有以下几点特点:
1. 是一个可重入锁,能保证同一个线程多次加锁。
2. 可以实现公平琐和非公平琐,初始化的时候可以指定类型。
3. 默认是非公平琐,线程每次再加入同步队列之前,都会尝试获取琐。
4. ReentrantLock类实现的琐可以看作是synchronized的升级版,可以手动获取琐和释放锁,并且可以绑定多个条件(基于AQS的Condition)。
更多精彩内容,敬请扫描下方二维码,关注我的微信公众号【Java觉浅】,获取第一时间更新哦!