> 文章列表 > ReetrantLock源码剖析_03公平锁、非公平锁

ReetrantLock源码剖析_03公平锁、非公平锁

ReetrantLock源码剖析_03公平锁、非公平锁

一直努力就会有offer,一直努力就会有offer,一直努力就会有offer!

文章目录

    • ReetrantLock公平锁代码解析
    • ReetrantLock公平锁执行流程
    • ReetrantLock非公平锁代码解析
    • ReetrantLock非公平锁执行流程
    • 公平锁与非公平锁的比较

ReetrantLock公平锁代码解析

ReetrantLock的加锁是通过lock()方法实现的

public void lock() {sync.lock();}

ReetrantLock的构造器传入参数为true时,ReetrantLock就以公平锁的方式进行工作,因此当ReetrantLock以公平锁方式进行加锁时,调用的时FairSynclock()方法

final void lock() {acquire(1);}

FairSynclock()方法调用AQS acquire()方法实现对资源的加锁,参数1表示ReetrantLock加锁的重进入次数为1

ReetrantLock公平锁执行流程

ReetrantLock源码剖析_03公平锁、非公平锁
释放锁:

 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非公平锁代码解析

与公平锁不同的是,非公平锁的加锁是通过调用NonFairSynclock()方法来实现的

/* 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方法不同的是,NonFairSynclock()方法会直接进行CAS操作,如果CAS成功,就直接将锁的持有者设置为当前线程,体现了非公平锁的非公平性。如果NonFairSynclock()方法执行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;}

nonfairTryAcquiretryAcquire方法不同的是,tryAcquire方法多调用了一个hasQueuedPredecessors方法,这个方法就是用来判断同步队列中是否存在比当前线程先入队的线程,公平锁需要遵循一个先来后到,而非公平锁不会遵守秩序,直接加锁。如果非公平锁加锁失败,程序就和公平锁的执行顺序类似了。

ReetrantLock非公平锁执行流程

ReetrantLock源码剖析_03公平锁、非公平锁

公平锁与非公平锁的比较

公平锁侧重的是公平性,非公平锁侧重的是并发性
非公平锁对锁的竞争是抢占式的,而且可以两次获取锁,这就提高了获取锁的可能性。好处有两点:

  • 线程不用加入同步队列就可以进行加锁操作,可以免去构造Node节点并加入阻塞队列的操作,还能减少线程阻塞与唤醒的开销。在高并发的情况下,如果线程持有锁的时间短于线程入队及阻塞的时间开销,那么这种抢占式的特性对并发性能的提升会更加明显。
  • 减少CAS竞争,如果必须要加入阻塞队列才能获取锁,那么入队时CAS竞争会很激烈,CAS操作失败虽然不会导致线程挂起,但是重试操作也会给CPU带来非常大的开销。

在这里插入图片描述
参考书籍:Java面试一站到底