JUC锁——互斥锁之非公平锁

xiaoxiao2025-05-22  35

获取非公平锁

  非公平锁和公平锁在获取锁的方法上,流程是一样的。它们的区别主要表现在“尝试获取锁的机制不同”。简单点说,“公平锁”在每次尝试获取锁时,都是采用公平策略(根据等待队列依次排队等待);而“非公平锁”在每次尝试获取锁时,都是采用的非公平策略(无视等待队列,直接尝试获取锁,如果锁是空闲的,即可获取状态,则获取锁)。下面,通过代码分析一下获取非公平锁的流程: 1、lock() 非公平锁的lock()在ReentrantLock.java的NonfairSync类中实现,它的源码如下:

final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }

说明:lock()会先通过compareAndSet(0, 1)来判断“锁”是不是空闲状态。是的话,“当前线程”直接获取“锁”;否则的话,调用acquire(1)获取锁。 (01) compareAndSetState()是CAS函数,它的作用是比较并设置当前锁的状态。若锁的状态值为0,则设置锁的状态值为1 (02) setExclusiveOwnerThread(Thread.currentThread())的作用是,设置“当前线程”为“锁”的持有者

“公平锁”和“非公平锁”中lock()的对比:

公平锁——公平锁的lock()函数,会直接调用acquire(1) 非公平锁——非公平锁会先判断当前锁的状态是不是空闲,是的话,就不排队,而是直接获取锁

2、acquire() acquire()在AQS中实现的,它的源码如下:

public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }

① “当前线程”首先通过tryAcquire()尝试获取锁。尝试成功的话,直接返回;尝试失败的话,进入到等待队列依次排序,然后获取锁 ②“当前线程”尝试失败的情况下,会先通过addWaiter(Node.EXCLUSIVE)来将“当前线程”加入到"CLH队列(非阻塞的FIFO队列)"末尾 ③ 然后,调用acquireQueued()获取锁。在acquireQueued()中,当前线程会等待它在“CLH队列”中前面的所有线程执行并释放锁之后,才能获取锁并返回。如果“当前线程”在休眠等待过程中被中断过,则调用selfInterrupt()来自己产生一个中断

“公平锁”和“非公平锁”关于acquire()的对比:

公平锁和非公平锁,只有tryAcquire()函数的实现不同;即它们尝试获取锁的机制不同。这就是我们所说的“它们获取锁的策略不同”

非公平锁的tryAcquire()在ReentrantLock.java的NonfairSync类中实现,源码如下:

protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }

nonfairTryAcquire()在ReentrantLock.java的Sync类中实现,源码如下:

final boolean nonfairTryAcquire(int acquires) { // 获取“当前线程” final Thread current = Thread.currentThread(); // 获取“锁”的状态 int c = getState(); // c=0意味着“锁没有被任何线程所持有” if (c == 0) { // 若“锁没有被任何线程锁持有”,则通过CAS函数设置“锁”的状态为acquires // 同时,设置“当前线程”为锁的持有者 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; }

说明:根据代码,我们可以分析出,tryAcquire()的作用就是尝试去获取锁 ① 如果“锁”没有被任何线程持有,则通过CAS函数设置“锁”的状态为acquires,同时,设置“当前线程”为锁的持有者,然后返回true ②如果“锁”的持有者已经是当前线程,则将锁的状态更新 ③如果不是上面的两种情况,则认为尝试失败

“公平锁”和“非公平锁”关于tryAcquire()的对比:

公平锁和非公平锁,它们尝试获取锁的方式不同: 公平锁在尝试获取锁时,即使“锁”没有被任何线程所持有,它也会判断自己是不是CLH等待队列的表头;是的话,才获取锁; 而非公平锁在尝试获取锁时,如果“锁”没有被任何线程持有,则不管它在CLH队列的何处,它都直接获取锁。
释放非公平锁

非公平锁和公平锁在释放锁的方法和策略上是一样的。

总结

  公平锁和非公平锁的区别在获取锁的机制上。表现在,在尝试获取锁时:公平锁只有在当前线程是CLH等待队列的表头时,才获取锁;而非公平锁,只要当前锁处于空闲状态,则直接获取锁,而不管CLH等待队列中的顺序,只有当非公平锁尝试获取锁失败的时候,它才会像公平锁一样,进入CLH等待队列排序等待。

转载请注明原文地址: https://www.6miu.com/read-5030492.html

最新回复(0)