【后端面经-Java】公平锁和加锁流程

这篇具有很好参考价值的文章主要介绍了【后端面经-Java】公平锁和加锁流程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录
  • 1. 公平锁和非公平锁
    • 1.1 基本概念
    • 1.2 ReentrantLock 的公平锁和非公平锁
  • 2. 加锁流程
    • 2.1 ReentrantLock 和 AQS 的关系
    • 2.2 公平锁-加锁流程
    • 2.3 非公平锁-加锁流程
    • 2.4 加锁流程和性能的关系
  • 3. 面试问题模拟
  • 参考文献

1. 公平锁和非公平锁

1.1 基本概念

  • 公平锁:线程按照到来的先后顺序,排队等待使用资源。
  • 非公平锁:线程不一定按照先后顺序使用资源,而是可能出现“插队”的情况。

拿游乐场等待娱乐项目举例,普通游客只能按照先后顺序排队等待使用游乐设施,这就是公平锁,但是普通入口加上优速通,显然VIP游客可以快人一步,这就有点非公平锁的意思了。

1.2 ReentrantLock 的公平锁和非公平锁

在《【后端面经-Java】Synchronize和ReentrantLock区别》这篇博客中,我们对比过synchronizedReentrantLock的区别,其中synchronized是一种非公平锁,而ReentrantLock默认是非公平锁,但是也可设置为公平锁
具体设置方式如下:

//生成一个公平锁
static Lock lock = new ReentrantLock(true);
//生成一个非公平锁
static Lock lock = new ReentrantLock(false);
static Lock lock = new ReentrantLock();//默认参数就是false,这种写法也可

通过更改构造函数中的参数,我们可以修改ReentrantLock的锁类型,true表示公平锁,false表示非公平锁。构造函数具体代码如下所示:

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();//FairSync表示公平锁,NonfairSync表示非公平锁
}

2. 加锁流程

2.1 ReentrantLock 和 AQS 的关系

在【后端面经-Java】AQS详解这篇博客中,我们详细讲解了AQS的原理,其中提到了

AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock。

可就是说,ReentrantLock也是通过AQS来实现的,而自定义同步锁需要实现AQS框架中的tryAcquire()tryRelease()方法或者tryAcquireShared()tryReleaseShared()方法。

因此,ReentrantLock的加锁流程我们可用查看tryAcquire()方法了解。

2.2 公平锁-加锁流程

公平锁的tryAcquire()方法源码如下所示:

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;
        }
    } else if (current == getExclusiveOwnerThread()) {//如果有其他线程在等待或者资源不可用,线程进入等待态,排队等待
        int nextc = c + acquires;
        if (nextc < 0) {
            throw new Error("Maximum lock count exceeded");
        }
        setState(nextc);
        return true;
    }
    return false;
}

代码流程如下所示:

  • 查看是否有其他线程在等待资源。
  • 如果没有其他线程在等待,查看资源是否可用,如果资源可用,直接获取资源。
  • 如果有其他线程在等待或者资源不可用(正在被使用),线程乖乖排到队尾,并切换为等待唤醒的休眠态。

2.3 非公平锁-加锁流程

非公平锁的tryAcquire()方法源码如下所示:

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;
}

公平锁相比,非公平锁的加锁流程只是少了对其他线程是否等待的判断,因此,非公平锁的加锁流程如下所示:

  • 查看资源是否可用,如果资源可用,直接获取资源。
  • 如果资源不可用,不需要管是否有线程在排队,还是排在等待队列队尾。

2.4 加锁流程和性能的关系

公平锁能保证线程获取资源的公平性,但是性能较低;
而非公平锁虽然无法保障公平性,但是性能更高,因此在大多数情况下,我们都会使用非公平锁。

  • 关于“公平锁性能低,非公平锁性能高”的解释
    理解这个结论,我们需要知道公平锁和非公平锁申请资源的流程。
    • 对于公平锁,当一个线程创建之后,它会看是否有其他线程在等待资源,也就是看看排队队伍里面有没有人,如果有其他线程在等待,它就乖乖排到队尾,并切换为等待唤醒的休眠态。而如果没有其他线程在等待,它就直接获取资源。
    • 对于非公平锁,当一个线程创建之后,它会直接试着去获取资源,不管队伍里有没有人,如果这个时候正好资源被释放,那么非公平锁因为是抢着使用资源的,提出资源申请比首个在队列中等待的线程要早,因此资源会直接给它。如果获取资源失败,它才会乖乖去队尾排队等待。

对于线程状态的切换,从休眠态到就绪态,这部分是需要时间进行上下文切换的,因此,公平锁每次都直接进入休眠态等待被唤醒,这本身就是很耗费时间的事情,因此我们才说公平锁性能低,非公平锁性能高

(非公平锁虽然不公平,但是性能高,真的是很讽刺的一种情况呐。)

3. 面试问题模拟

Q:公平锁是什么?加锁流程是什么?
A:公平锁是指在资源获取过程中,线程按照到来顺序排队使用资源的一种锁机制,而非公平锁则可能出现不按顺序的随机获取情况。
公平锁的加锁流程体现在tryAcquire()源码部分,当一个线程节点创建之后,它会判断当前是否有其他线程在等待以及资源是否可用,如果两个条件都满足,它则获取资源,如果不满足,它则乖乖排到队尾,等待被唤醒。文章来源地址https://www.toymoban.com/news/detail-508108.html

参考文献

  1. 面试突击46:公平锁和非公平锁有什么区别?
  2. 讲一讲公平锁和非公平锁,为什么要“非公平”?
  3. ReentrantLock 加锁过程源码详解

到了这里,关于【后端面经-Java】公平锁和加锁流程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • mysql(八)事务隔离级别及加锁流程详解

    锁是计算机用以协调多个进程间并发访问同一共享资源的一种机制 在数据库中,除传统计算资源(CPU、RAM、IO等)的争抢,数据也是一种供多用户共享的资源。 如何保证数据并发访问的一致性,有效性,是所有数据库必须要解决的问题。 锁冲突也是影响数据库并发访问性能

    2024年02月11日
    浏览(51)
  • Java里面加锁的方式

    使用synchronized可以实现对代码块或方法的加锁。当一个线程获取到锁后,其他线程将被阻塞,直到该线程释放锁。 示例代码如下: ReentrantLock是Java提供的显式锁(Explict Lock)实现类。它使用lock()和unlock()方法来加锁和释放锁,可以实现更灵活的加锁操作。 示例代码如下

    2024年02月10日
    浏览(59)
  • 【后端面经-Java】Java基本概念

    目录 【后端面经-Java】Java基本概念 1. 面向对象和面向过程 1.1 概念 1.2 优缺点对比 2. C++和Java的区别 2.1 \\\"去其糟粕\\\"——Java同学,你对C++的这些东西有什么意见? 2.2 改进之处————Java同学,你的这个论文,,,创新点是什么? 面试模拟 参考资料 面向过程:将一个事件拆分

    2024年02月16日
    浏览(41)
  • Java多线程:读写锁和两种同步方式的对比

    读写锁ReentrantReadWriteLock概述 大型网站中很重要的一块内容就是数据的读写,ReentrantLock虽然具有完全互斥排他的效果(即同一时间只有一个线程正在执行lock后面的任务),但是效率非常低。所以在JDK中提供了一种读写锁ReentrantReadWriteLock,使用它可以加快运行效率。 读写锁表

    2023年04月09日
    浏览(36)
  • Java后端面试题——Mysql篇

    在Mysql中,如何定位慢查询呢? 表象:页面加载过慢、接口压测响应时间过长(超过1s) 原因: 聚合查询 多表查询 表数据量过大查询 深度分页查询 方案:MySQL自带慢日志 需要在MySQL的配置文件(/etc/my.cnf)中配置如下信息: # 开启MySQL慢日志查询开关 slow_query_log=1 # 设置慢日

    2024年02月12日
    浏览(37)
  • 【后端面经-Java】AQS详解

    目录 1. AQS是什么? 2. AQS核心思想 2.1 基本框架 2.1.1 资源state 2.1.2 CLH双向队列 2.2 AQS模板 3. 源码分析 3.1 acquire(int) 3.1.1 tryAcquire(int) 3.1.2 addWaiter(Node.EXCLUSIVE) 3.1.3 acquireQueued(Node node, int arg) 3.2 release(int) 3.2.1 tryRelease(int) 3.2.2 unparkSuccessor(h) 3.3 acquireShared(int)和releaseShared(int) 3.3.1 acq

    2024年02月11日
    浏览(53)
  • 【后端面经-Java】Java创建线程的方法简介

    目录 1. 线程的基本概念 1.1 线程 1.2 线程状态和生命周期 2. 创建线程的四种方法 2.1 继承Thread类 2.2 实现Runnable接口 2.3 实现Callable接口 2.4 使用线程池 3. 参考资料 学过操作系统的同学应该不陌生,线程是计算机中的最小调度单元,一个进程可以有多个线程,执行并发操作,提

    2024年02月09日
    浏览(40)
  • 2023年精选出来的Java后端面试题

    1、Java与C++的区别? Java源码会先经过编译器编译成字节码(class文件),然后由JVM中内置的解释器解释成机器码。而C++源码直径过一次编译,直接在编译的过程中链接了,形成机器码 C++比Java执行效率快,但是Java可以利用JVM跨平台 Java是纯面向对象的语言,所有代码都必须在勒种

    2024年02月09日
    浏览(39)
  • 【后端面经-java】java线程池满的处理策略

    目录 1. 线程池介绍 1.1 基本作用 1.2 处理流程 1.3 线程池大小设置 1.4 线程池参数 2. 线程池满的处理策略 2.1 默认--拒绝策略handler 3. 参考资料 对多个线程使用的资源进行集中管理。 降低资源消耗: 复用线程,降低线程创建和销毁造成的消耗; 线程资源管理 提高管理效率;

    2024年02月09日
    浏览(33)
  • 【后端面经-Java】Synchronize和ReentrantLock区别

    1.1 线程安全锁 Synchronize(同步锁)和ReentrantLock(可重入锁)都是Java中的常用锁,都是用来保证线程安全的。 两者都是同步锁,且都是阻塞同步。 阻塞同步:当一个线程获取锁后,其他线程只能等待(进入阻塞态),等待获取锁的线程释放锁后,其他线程才能获取锁。 1.2

    2024年02月11日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包