首页 > 其他分享 >面试被问ReentrantLock的公平锁与非公平锁

面试被问ReentrantLock的公平锁与非公平锁

时间:2023-04-28 19:33:52浏览次数:29  
标签:CAS lock ReentrantLock 线程 公平 与非 static final


关注Java后端技术栈

回复“面试”获取最新资料

面试被问ReentrantLock的公平锁与非公平锁的区别以及实现。
建议先阅读Java中的锁原理、锁优化、CAS、AQS,看这篇就对了!

案例

public class LockDemo {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        try {
            lock.lock();
            System.out.println("获得锁");
        } finally {
            lock.unlock();
            System.out.println("释放锁");
        }
    }
}

运行结果

面试被问ReentrantLock的公平锁与非公平锁_技术栈

先看ReentrantLock的构造方法源码:

private final Sync sync;
public ReentrantLock() {
     sync = new NonfairSync();
}

所以,记住默认是非公平锁,有在new 的时候参数为true的时候才变成了公平锁。

再看Sync是个什么东东

abstract static class Sync extends AbstractQueuedSynchronizer {
 }

可以看出这个是继承于AQS的一个静态抽象内部类。有两个子类

面试被问ReentrantLock的公平锁与非公平锁_公平锁_02

这两个类也就是我们所说的公平锁与非公平锁。

还可以通过手动设置公平锁与非公平锁

public ReentrantLock(boolean fair) {
     sync = fair ? new FairSync() : new NonfairSync();
}

FairSync公平锁

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }
       //尝试直接获取锁,返回值是boolean,代表是否获取到锁
       //返回true:
        //1.没有线程在等待锁;
        //2.重入锁,线程本来就持有锁,也就可以理所当然可以直接获取
        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;
                }
            }
            //判断是有锁的线程是否为当前线程。
            //可重入就在这里体现的,同一个线程多次调用lock方法
            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()判断队列是否还有别的线程在等待锁,没有的话就尝试获取lock 。

compareAndSetState(0, acquires)尝试获取锁

NonfairSync非公平锁

static final class NonfairSync extends Sync {
     private static final long serialVersionUID = 7316153563782823691L;

     /**
      * Performs lock.  Try immediate barge, backing up to normal
      * acquire on failure.
      */
     final void lock() {
         //不管AQS队列有没有等待的线程,直接开始抢
         if (compareAndSetState(0, 1))
             setExclusiveOwnerThread(Thread.currentThread());
         else
            acquire(1);
     }

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

compareAndSetState(0, 1)首先用一个CAS操作,判断state是否是0(表示当前锁未被占用),如果是0则把它置为1,并且

setExclusiveOwnerThread(Thread.currentThread());

设置当前线程为该锁的独占线程,表示获取锁成功。当多个线程同时尝试占用同一个锁时,CAS操作只能保证一个线程操作成功,剩下的只能乖乖的去排队啦。

总结

公平锁和非公平锁只有两处不同:

  1. 非公平锁在调用 lock 后,首先就会调用 CAS 进行一次抢锁,如果这个时候恰巧锁没有被占用,那么直接就获取到锁返回了。
  2. 非公平锁在 CAS 失败后,和公平锁一样都会进入到 tryAcquire 方法,在 tryAcquire 方法中,如果发现锁这个时候被释放了(state == 0),非公平锁会直接 CAS 抢锁,但是公平锁会判断等待队列是否有线程处于等待状态,如果有则不去抢锁,乖乖排到后面。

公平锁和非公平锁就这两点区别,如果这两次 CAS 都不成功,那么后面非公平锁和公平锁是一样的,都要进入到阻塞队列等待唤醒。

相对来说,非公平锁会有更好的性能,因为它的吞吐量比较大。当然,非公平锁让获取锁的时间变得更加不确定,可能会导致在阻塞队列中的线程长期处于饥饿状态。
推荐阅读目前行业薪资原来是这样的...面试被问ReentrantLock的公平锁与非公平锁_技术栈_03一次蚂蚁金服的辛酸面试历程
5 个刁钻的 String 面试题!

关注Java后端技术栈后发送  干货  免费获取下面超500G的资料

面试被问ReentrantLock的公平锁与非公平锁_技术栈_04

标签:CAS,lock,ReentrantLock,线程,公平,与非,static,final
From: https://blog.51cto.com/u_11702014/6235409

相关文章

  • 参数与非参数检验:理解差异并正确使用
    数据科学是一个快速发展的领域,它在很大程度上依赖于统计技术来分析和理解复杂的数据集。这个过程的一个关键部分是假设检验,它有助于确定从样本中获得的结果是否可以推广到总体。在这篇文章中,我们将探讨参数与非参数检验之间的区别,提供示例以更好地理解它们的用例,并总结关键要点。......
  • synchronized关键字、ReentrantLock
    synchronized是Java中的一个关键字,同步,主要解决的是多个线程之间访问资源的同步性,可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。在Java早期版本中,synchronized属于重量级锁,效率低下。这是因为监视器锁(monitor)是依赖于底层的操作系统的Mutex......
  • ReentrantLock与AQS
    参考:《对线面试官》 公平锁和非公平锁公平锁:在竞争环境下,先到的线程一定比后到的线程更快获取到锁非公平锁:先到的线程未必能够先获取锁 怎么实现可以使用先进先出队列 公平锁:竞争线程先入队,持有锁的线程释放锁后,唤醒队列的下一个线程去获取锁 (先排队) 非公平锁:竞......
  • 浅析ReentrantLock和AQS
          AQS的全称是AbstractQueuedSynchronizer,这是AQS框架的核心抽象类。ReentrantLock有三个内部类:Sync、NonfairSync、FairSync。FairSync代表了公平锁,NonfairSync代表了非公平锁,NonfairSync和FairSync都继承自Sync,Sync继承自AbstractQueuedSynchronizer。      AQ......
  • 极简cfs公平调度算法
    1.说明1>linux内核关于task调度这块是比较复杂的,流程也比较长,要从源码一一讲清楚很容易看晕2>本篇文章主要是讲清楚cfs公平调度算法如何将task在时钟中断驱动下切换调度,所以与此无关的代码一律略过3>本篇只讲最简单的task调度,略过组调度,组调度在下一篇《极简组调度-CGroup......
  • 托管与非托管转换-Marshal 类
    Marshal是一个方法集合,主要应用在C#和非托管代码交互时,主要有如下方法:分配非托管内存复制非托管内存块将托管类型转换为非托管类型其他方法(与非托管代码交互时)常用方法IntPtrptr=xxxx;Datadata=newData();objectobj=data;ptr====>dataMarshal.......
  • 【LeetCode回溯算法#extra01】集合划分问题【火柴拼正方形、划分k个相等子集、公平发
    火柴拼正方形https://leetcode.cn/problems/matchsticks-to-square/你将得到一个整数数组matchsticks,其中matchsticks[i]是第i个火柴棒的长度。你要用所有的火柴棍拼成一个正方形。你不能折断任何一根火柴棒,但你可以把它们连在一起,而且每根火柴棒必须使用一次。如......
  • “JUC锁”02之 互斥锁ReentrantLock
    本章对ReentrantLock包进行基本介绍,这一章主要对ReentrantLock进行概括性的介绍,内容包括:ReentrantLock介绍ReentrantLock函数列表ReentrantLock示例在后面的两章,会分别介绍ReentrantLock的两个子类(公平锁和非公平锁)的实现原理。转载请注明出处:http://www.cnblogs.com/skywang123......
  • .NET与非托管代码交互操作 — IntPtr
    在.NET中,IntPtr是一个结构体,封装于mscorlib.dll程序集,表示一个指针或句柄类型的整数值。它的作用类似于C/C++中的void*指针类型,可以存储指向任意数据类型的内存地址,定义如下图IntPtr通常用于与非托管代码进行交互,比如调用Win32API函数,由于非托管代码使用指针或句柄来访问内存......
  • 2563. 统计公平数对的数目
    题目链接:2563.统计公平数对的数目方法:排序+二分解题思路(1)先对数组进行排序,排序之后并不影响公平数对的数目;(2)对于任意一个\(j\),它的公平数对\((i,j)\)满足\(lower-nums[j]≤nums[i]≤upper-nums[j]\),即在\([0,j]\)范围中找满足条件的\(i\)的个数,通过二分......