ReentrantLock是lock接口的一个实现类,里面实现了可重入锁和公平锁非公平锁
ReentrantLock公平锁和不公平锁实现原理
公平锁会获取锁时会判断阻塞队列里是否有线程再等待,若有获取锁就会失败,并且会加入阻塞队列
非公平锁获取锁时不会判断阻塞队列是否有线程再等待,所以对于已经在等待的线程来说是不公平的,但如果是因为其它原因没有竞争到锁,它也会加入阻塞队列
进入阻塞队列的线程,竞争锁时都是公平的,应为队列为先进先出(FIFO)
默认实现的是非公平锁
public ReentrantLock() { //非公平锁 sync = new NonfairSync(); }
还提供了另外一种方式,可传入一个boolean值,true时为公平锁,false时为非公平锁
//公平锁 public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
非公平锁
非公平锁获取锁nonfairTryAcquire
方法,对锁状态进行了判断,并没有把锁加入同步队列中
static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; final void lock() { //比较当前状态 如果持有者是当前线程 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else //如果不是 尝试获取锁 acquire(1); } protected final boolean tryAcquire(int acquires) { //获取锁 return nonfairTryAcquire(acquires); } } 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; }
公平锁
代码和nonfairTryAcquire
唯一的不同在于增加了hasQueuedPredecessors
方法的判断
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; protected final boolean tryAcquire(int acquires) { //获取当前线程 final Thread current = Thread.currentThread(); int c = getState(); //判断当前对象是否被持有 if (c == 0) { //如果等待队列为空 并且使用CAS获取锁成功 否则返回false然后从队列中获取节点 if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) { //把当前线程持有 setExclusiveOwnerThread(current); return true; } } //若被持有 判断锁是否是当前线程 可重入锁的关键代码 else if (current == getExclusiveOwnerThread()) { //计数加1 返回 int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } //不是当前线程持有 执行 return false; } }
acquire()获取锁
public final void acquire(int arg) { //如果当前线程尝试获取锁失败并且 加入把当前线程加入了等待队列 if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //先中断当前线程 selfInterrupt(); }
关键代码
就是tryAcquire
方法中hasQueuedPredecessors
判断队列是否有其他节点
public final boolean hasQueuedPredecessors() { Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
可重入性实现原理
在线程获取锁的时候,如果已经获取锁的线程是当前线程的话则直接再次获取成功
由于锁会被获取n次,那么只有锁在被释放同样的n次之后,该锁才算是完全释放成功
1、获取锁方法
protected final boolean tryAcquire(int acquires) { //获取当前线程 final Thread current = Thread.currentThread(); int c = getState(); //判断当前对象是否被持有 if (c == 0) { //...略 } //若被持有 判断锁是否是当前线程 可重入锁的关键代码 else if (current == getExclusiveOwnerThread()) { //计数加1 返回 int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } //不是当前线程持有 执行 return false; }
每次如果获取到的都是当前线程这里都会计数加1
释放锁
protected final boolean tryRelease(int releases) { //每次释放都减1 int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; //等于0才释放锁成功 if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; }
如果锁被获取n次,也要释放了n次,只有完全释放才会返回false