ReetrantLock源码剖析_03公平锁、非公平锁
一直努力就会有offer,一直努力就会有offer,一直努力就会有offer!
文章目录
-
- ReetrantLock公平锁代码解析
- ReetrantLock公平锁执行流程
- ReetrantLock非公平锁代码解析
- ReetrantLock非公平锁执行流程
- 公平锁与非公平锁的比较
ReetrantLock公平锁代码解析
ReetrantLock
的加锁是通过lock()
方法实现的
public void lock() {sync.lock();}
当ReetrantLock
的构造器传入参数为true
时,ReetrantLock
就以公平锁的方式进行工作,因此当ReetrantLock
以公平锁方式进行加锁时,调用的时FairSync
的lock()
方法
final void lock() {acquire(1);}
FairSync
的lock()
方法调用AQS
的 acquire()
方法实现对资源的加锁,参数1表示ReetrantLock
加锁的重进入次数为1
ReetrantLock公平锁执行流程
释放锁:
public void unlock() {sync.release(1);}
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()
方法由其子类实现(已经解读过)
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;}
ReetrantLock非公平锁代码解析
与公平锁不同的是,非公平锁的加锁是通过调用NonFairSync
的lock()
方法来实现的
/* Performs lock. Try immediate barge, backing up to normal* acquire on failure.*/final void lock() {//CAS操作if (compareAndSetState(0, 1))//把锁的持有者设置为当前线程setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);}
与公平锁FairSync
中的lock
方法不同的是,NonFairSync
的lock()
方法会直接进行CAS
操作,如果CAS
成功,就直接将锁的持有者设置为当前线程,体现了非公平锁的非公平性。如果NonFairSync
的lock()
方法执行CAS
失败了,就说明,当前环境中有其他线程正在争夺锁,并且当前线程加锁失败。接下来,它会调用CAS
中的acquire
方法:
public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}
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) {//没有调用hasQueuedPredecessors方法if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}
protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {//多调用了一个hasQueuedPredecessors方法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;}
nonfairTryAcquire
与tryAcquire
方法不同的是,tryAcquire
方法多调用了一个hasQueuedPredecessors
方法,这个方法就是用来判断同步队列中是否存在比当前线程先入队的线程,公平锁需要遵循一个先来后到,而非公平锁不会遵守秩序,直接加锁。如果非公平锁加锁失败,程序就和公平锁的执行顺序类似了。
ReetrantLock非公平锁执行流程
公平锁与非公平锁的比较
公平锁侧重的是公平性
,非公平锁侧重的是并发性
。
非公平锁对锁的竞争是抢占式的,而且可以两次获取锁,这就提高了获取锁的可能性。好处有两点:
- 线程不用加入同步队列就可以进行加锁操作,可以免去构造
Node
节点并加入阻塞队列的操作,还能减少线程阻塞与唤醒的开销。在高并发的情况下,如果线程持有锁的时间短于线程入队及阻塞的时间开销,那么这种抢占式的特性对并发性能的提升会更加明显。 - 减少
CAS
竞争,如果必须要加入阻塞队列才能获取锁,那么入队时CAS竞
争会很激烈,CAS
操作失败虽然不会导致线程挂起,但是重试操作也会给CPU
带来非常大的开销。
参考书籍:Java面试一站到底