了解这两个接口后,阿里多线程面试题秒AC

简介: 大家好,我是指北君我们知道,阿里面试时非常喜欢考Java多线程编程题,如果你AC不了,可能会给面试官留下一个基础不扎实的印象,影响到你offer的收割,想当年指北君面阿里时就因为秒AC了一道多线程面试题,让面试官刮目相看,所以我们需要重视Java多线程编程。

一般在解决多线程编程题时,我们都离不开JUC并发包下的各种工具类,特别是ReentrantLock锁,它能提供互斥与线程同步的能力,那它是如何获得这个能力的呢?今天指北君就来详细说说给它提供强大能力的两大接口。(PS:文末有当年指北君面试阿里的多线程编程原题以及答案喔)

我们知道,并发领域中有两大核心问题:互斥与同步问题,Java在1.5版本之前,是提供了synchronized来实现的。synchronized是内置锁,虽然在大部分情况下它都能很好的工作,但是依然还是会存在一些局限性,除了当时1.5版本的性能问题外(1.6版本后,synchronized的性能已经得到了很大的优化),还有如下两个问题:

  1. 无法解决死锁问题
  2. 最多使用一个条件变量

所以针对这些问题,Doug Lea在并发包中增加了两个接口Lock和Condition来解决这两个问题,所以指北君今天就说说这两个接口是如何解决synchronized中的这两个问题的。


一. Lock接口


1.1 介绍


在我们分析Lock接口是如何解决死锁问题之前,我们先看看死锁是如何产生的。死锁的产生需要满足下面四个条件:

  1. 互斥:共享资源同一时间只能被一个线程占用
  2. 不可抢占:其他线程不能强行占有另一个线程的资源
  3. 占有且等待:线程在等待其他资源时,不释放自己已占有的资源
  4. 循环等待:线程1和线程2互相占有对方的资源并相互等待

所以,我们只需要破坏上面条件中的任意一个,即可打破死锁。但需要注意的是,互斥条件是不能破坏的,因为使用锁的目的就是为了互斥。所以Lock接口通过破坏掉 "不可抢占"这个条件来解决死锁,具体如下:

  1. 非阻塞获取锁:尝试获取锁,如果失败了就立刻返回失败,这样就可以释放已经持有的其他锁
  2. 响应中断:如果发生死锁后,此线程被其他线程中断,则会释放锁,解除死锁
  3. 支持超时:一段时间内获取不到锁,就返回失败,这样就可以释放之前已经持有的锁

接下来我们具体看看接口代码吧。


1.2 源码解读

30.png

31.png

还需要额外注意的一点,使用synchronized作为锁时,我们是不需要考虑释放锁的,但Lock是属于显示锁,是需要我们手动释放锁的。我们一般在finally块中调用lock.unlock()手动释放锁,具体形式如下:

32.png我们最后通过一张图来总结下Lock接口:


33.jpg


二. Condition接口


2.1 介绍

针对synchronized最多只能使用一个条件变量的问题,Condition接口提供了解决方案。但是为什么多个条件变量就比一个条件变量好呢?我们先来看看synchronized使用一个条件变量时会有什么弊端。

一个synchronized内置锁只对应一个等待容器(wait set),当线程调用wait方法时,会把当前线程放入到同一个等待容器中,当我们需要根据某些特定的条件来唤醒符合条件的线程时,我们只能先从等待容器里唤醒一个线程后,再看是否符合条件。如果不符合条件,则需要将此线程继续wait,然后再去等待容器中获取下一个线程再判断是否满足条件。这样会导致许多无意义的cpu开销。

我们可以看到Lock接口中有个newCondition()的方法:

34.png

通过这个方法,一个锁可以建立多个Conditiion,每个Condtition都有一个容器来保存相应的等待线程,拿到锁的线程根据特定的条件唤醒对应的线程时,只需要去唤醒对应的Contition内置容器中的线程即可,这样就可以减少无意义的CPU开销。然后我们具体看看Condition接口的源码。

2.2 源码解读

100.png36.png


需要注意的是,Object类的等待方法是没有返回值的,但Condtition类中的部分等待方法是有返回值的。awaitNanos(long nanosTimeout)返回了剩余等待的时间;await(long time, TimeUnit unit)返回boolean值,如果返回false,则说明是因为超时返回的,否则返回true。为什么增加返回值?为了就是帮助我们弄清楚方法返回的原因。


四. 阿里多线程考题


最后我们通过实现了Lock和Condition接口能力的ReentrantLock类来解决阿里多线程面试题。

题目是使用三个线程循环打印ABC,一共打印50次。我们直接上答案:

37.png38.png39.png


五. 总结


Lock与Condition接口就说完了,最后指北君再总结一下:

针对synchronized内置锁无法解决死锁、只有一个条件变量等问题,Doug Lea在Java并发包中增加了Lock和Condition接口来解决。对于死锁问题,Lock接口增加了超时、响应中断、非阻塞三种方式来获取锁,从而避免了死锁。针对一个条件变量问题,Condtition接口通过一把锁可以创建多个条件变量的方式来解决。最后我们通过一个阿里面试题来说明了Lock和Condition接口所提供的能力。

今天就到这了,我是指北君,我们下篇文章见~


相关文章
|
6天前
|
存储 算法 架构师
阿里面试:PS+PO、CMS、G1、ZGC区别在哪?什么是卡表、记忆集、联合表?问懵了,尼恩来一个 图解+秒懂+史上最全的答案
阿里面试:PS+PO、CMS、G1、ZGC区别在哪?什么是卡表、记忆集、联合表?问懵了,尼恩来一个 图解+秒懂+史上最全的答案
|
21天前
|
存储 NoSQL Redis
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 + 无锁架构 + EDA架构 + 异步日志 + 集群架构
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 + 无锁架构 + EDA架构 + 异步日志 + 集群架构
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 +  无锁架构 +  EDA架构  + 异步日志 + 集群架构
|
2月前
|
存储 SQL 算法
阿里面试:每天新增100w订单,如何的分库分表?这份答案让我当场拿了offer
例如,在一个有 10 个节点的系统中,增加一个新节点,只会影响到该新节点在哈希环上相邻的部分数据,其他大部分数据仍然可以保持在原节点,大大减少了数据迁移的工作量和对系统的影响。狠狠卷,实现 “offer自由” 很容易的, 前段时间一个武汉的跟着尼恩卷了2年的小伙伴, 在极度严寒/痛苦被裁的环境下, offer拿到手软, 实现真正的 “offer自由”。在 3 - 5 年的中期阶段,随着业务的稳定发展和市场份额的进一步扩大,订单数据的增长速度可能会有所放缓,但仍然会保持在每年 20% - 30% 的水平。
阿里面试:每天新增100w订单,如何的分库分表?这份答案让我当场拿了offer
|
3月前
|
监控 Kubernetes Java
阿里面试:5000qps访问一个500ms的接口,如何设计线程池的核心线程数、最大线程数? 需要多少台机器?
本文由40岁老架构师尼恩撰写,针对一线互联网企业的高频面试题“如何确定系统的最佳线程数”进行系统化梳理。文章详细介绍了线程池设计的三个核心步骤:理论预估、压测验证和监控调整,并结合实际案例(5000qps、500ms响应时间、4核8G机器)给出具体参数设置建议。此外,还提供了《尼恩Java面试宝典PDF》等资源,帮助读者提升技术能力,顺利通过大厂面试。关注【技术自由圈】公众号,回复“领电子书”获取更多学习资料。
|
2月前
|
缓存 NoSQL Java
阿里面试:DDD 落地,遇到哪些 “拦路虎”?如何破局?
为每个子领域定义限界上下文(bounded context),限界上下文是一个清晰定义了领域模型的边界的范围。在限界上下文内,领域模型的概念是一致的,但不同限界上下文之间可以有不同的模型和语言。界限上下文,基本可以对应到 落地层面的 微服务。这就是 DDD 建模和 微服务架构, 能够成为孪生兄弟、 天然统一的原因。具体的方法论和落地实操,请参考 《第34章视频 DDD学习圣经》DDD 战略设计的第一步就是统一语言,也叫通用语言(UBIQUITOUS LANGUAGE),用于定义上下文。
阿里面试:DDD 落地,遇到哪些 “拦路虎”?如何破局?
|
2月前
|
数据采集 Java Linux
面试大神教你:如何巧妙回答线程优先级这个经典考题?
大家好,我是小米。本文通过故事讲解Java面试中常见的线程优先级问题。小明和小华的故事帮助理解线程优先级:高优先级线程更可能被调度执行,但并非越高越好。实际开发需权衡业务需求,合理设置优先级。掌握线程优先级不仅能写出高效代码,还能在面试中脱颖而出。最后,小张因深入分析成功拿下Offer。希望这篇文章能助你在面试中游刃有余!
57 4
面试大神教你:如何巧妙回答线程优先级这个经典考题?
|
2月前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
174 14
|
2月前
|
算法 NoSQL 应用服务中间件
阿里面试:10WQPS高并发,怎么限流?这份答案让我当场拿了offer
在 Nacos 的配置管理界面或通过 Nacos 的 API,创建一个名为(与配置文件中 dataId 一致)的配置项,用于存储 Sentinel 的流量控制规则。上述规则表示对名为的资源进行流量控制,QPS 阈值为 10。resource:要保护的资源名称。limitApp:来源应用,default表示所有应用。grade:限流阈值类型,1 表示 QPS 限流,0 表示线程数限流。count:限流阈值。strategy:流控模式,0 为直接模式,1 为关联模式,2 为链路模式。
阿里面试:10WQPS高并发,怎么限流?这份答案让我当场拿了offer
|
2月前
|
缓存 安全 Java
面试中的难题:线程异步执行后如何共享数据?
本文通过一个面试故事,详细讲解了Java中线程内部开启异步操作后如何安全地共享数据。介绍了异步操作的基本概念及常见实现方式(如CompletableFuture、ExecutorService),并重点探讨了volatile关键字、CountDownLatch和CompletableFuture等工具在线程间数据共享中的应用,帮助读者理解线程安全和内存可见性问题。通过这些方法,可以有效解决多线程环境下的数据共享挑战,提升编程效率和代码健壮性。
119 6
|
3月前
|
算法 安全 Java
Java线程调度揭秘:从算法到策略,让你面试稳赢!
在社招面试中,关于线程调度和同步的相关问题常常让人感到棘手。今天,我们将深入解析Java中的线程调度算法、调度策略,探讨线程调度器、时间分片的工作原理,并带你了解常见的线程同步方法。让我们一起破解这些面试难题,提升你的Java并发编程技能!
137 16

热门文章

最新文章