最常见并发面试题整理!(速度收藏)

前言

并发编程是面试中必问的知识点之一,所以本文整理了一些最为常见的并发面试题,一起来看吧~

1. synchronized的实现原理以及锁优化?

synchronized的实现原理

  • synchronized作用于「方法」或者「代码块」,保证被修饰的代码在同一时间只能被一个线程访问。

  • synchronized修饰代码块时,JVM采用「monitorenter、monitorexit」两个指令来实现同步

  • synchronized修饰同步方法时,JVM采用「ACC_SYNCHRONIZED」标记符来实现同步

  • monitorenter、monitorexit或者ACC_SYNCHRONIZED都是「基于Monitor实现」

  • 实例对象里有对象头,对象头里面有Mark Word,Mark Word指针指向了「monitor」

  • Monitor其实是一种「同步工具」,也可以说是一种「同步机制」

  • 在Java虚拟机(HotSpot)中,Monitor是由「ObjectMonitor实现」的。ObjectMonitor体现出Monitor的工作原理~

ObjectMonitor() {_header       = NULL;_count        = 0; // 记录线程获取锁的次数_waiters      = 0,_recursions   = 0;  //锁的重入次数_object       = NULL;_owner        = NULL;  // 指向持有ObjectMonitor对象的线程_WaitSet      = NULL;  // 处于wait状态的线程,会被加入到_WaitSet_WaitSetLock  = 0 ;_Responsible  = NULL ;_succ         = NULL ;_cxq          = NULL ;FreeNext      = NULL ;_EntryList    = NULL ;  // 处于等待锁block状态的线程,会被加入到该列表_SpinFreq     = 0 ;_SpinClock    = 0 ;OwnerIsThread = 0 ;}

ObjectMonitor的几个关键属性 _count、_recursions、_owner、_WaitSet、 _EntryList 体现了monitor的工作原理

锁优化

在讨论锁优化前,先看看JAVA对象头(32位JVM)中Mark Word的结构图吧~

Mark Word存储对象自身的运行数据,如「哈希码、GC分代年龄、锁状态标志、偏向时间戳(Epoch)」 等,为什么区分「偏向锁、轻量级锁、重量级锁」等几种锁状态呢?

在JDK1.6之前,synchronized的实现直接调用ObjectMonitor的enter和exit,这种锁被称之为「重量级锁」。从JDK6开始,HotSpot虚拟机开发团队对Java中的锁进行优化,如增加了适应性自旋、锁消除、锁粗化、轻量级锁和偏向锁等优化策略。

  • 偏向锁:在无竞争的情况下,把整个同步都消除掉,CAS操作都不做。

  • 轻量级锁:在没有多线程竞争时,相对重量级锁,减少操作系统互斥量带来的性能消耗。但是,如果存在锁竞争,除了互斥量本身开销,还额外有CAS操作的开销。

  • 自旋锁:减少不必要的CPU上下文切换。在轻量级锁升级为重量级锁时,就使用了自旋加锁的方式

  • 锁粗化:将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁。

举个例子,买门票进动物园。老师带一群小朋友去参观,验票员如果知道他们是个集体,就可以把他们看成一个整体(锁租化),一次性验票过,而不需要一个个找他们验票。

  • 锁消除:虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行削除。

有兴趣的朋友们可以看看我这篇文章:Synchronized解析——如果你愿意一层一层剥开我的心[1]

2. ThreadLocal原理,使用注意点,应用场景有哪些?

回答四个主要点:

  • ThreadLocal是什么?

  • ThreadLocal原理

  • ThreadLocal使用注意点

  • ThreadLocal的应用场景

ThreadLocal是什么?

ThreadLocal,即线程本地变量。如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个本地拷贝,多个线程操作这个变量的时候,实际是操作自己本地内存里面的变量,从而起到线程隔离的作用,避免了线程安全问题。

//创建一个ThreadLocal变量
static ThreadLocal<String> localVariable = new ThreadLocal<>();

ThreadLocal原理

ThreadLocal内存结构图:

由结构图是可以看出:

  • Thread对象中持有一个ThreadLocal.ThreadLocalMap的成员变量。

  • ThreadLocalMap内部维护了Entry数组,每个Entry代表一个完整的对象,key是ThreadLocal本身,value是ThreadLocal的泛型值。

对照着几段关键源码来看,更容易理解一点哈~

public class Thread implements Runnable {//ThreadLocal.ThreadLocalMap是Thread的属性ThreadLocal.ThreadLocalMap threadLocals = null;
}

ThreadLocal中的关键方法set()和get()

    public void set(T value) {Thread t = Thread.currentThread(); //获取当前线程tThreadLocalMap map = getMap(t);  //根据当前线程获取到ThreadLocalMapif (map != null)map.set(this, value); //K,V设置到ThreadLocalMap中elsecreateMap(t, value); //创建一个新的ThreadLocalMap}public T get() {Thread t = Thread.currentThread();//获取当前线程tThreadLocalMap map = getMap(t);//根据当前线程获取到ThreadLocalMapif (map != null) {//由this(即ThreadLoca对象)得到对应的Value,即ThreadLocal的泛型值ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value; return result;}}return setInitialValue();}

ThreadLocalMap的Entry数组

static class ThreadLocalMap {static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}
}

所以怎么回答「ThreadLocal的实现原理」?如下,最好是能结合以上结构图一起说明哈~

  • Thread类有一个类型为ThreadLocal.ThreadLocalMap的实例变量threadLocals,即每个线程都有一个属于自己的ThreadLocalMap。

  • ThreadLocalMap内部维护着Entry数组,每个Entry代表一个完整的对象,key是ThreadLocal本身,value是ThreadLocal的泛型值。

  • 每个线程在往ThreadLocal里设置值的时候,都是往自己的ThreadLocalMap里存,读也是以某个ThreadLocal作为引用,在自己的map里找对应的key,从而实现了线程隔离。

ThreadLocal 内存泄露问题

先看看一下的TreadLocal的引用示意图哈,

ThreadLocalMap中使用的 key 为 ThreadLocal 的弱引用,如下

弱引用:只要垃圾回收机制一运行,不管JVM的内存空间是否充足,都会回收该对象占用的内存。

弱引用比较容易被回收。因此,如果ThreadLocal(ThreadLocalMap的Key)被垃圾回收器回收了,但是因为ThreadLocalMap生命周期和Thread是一样的,它这时候如果不被回收,就会出现这种情况:ThreadLocalMap的key没了,value还在,这就会「造成了内存泄漏问题」

如何「解决内存泄漏问题」?使用完ThreadLocal后,及时调用remove()方法释放内存空间。

ThreadLocal的应用场景

  • 数据库连接池

  • 会话管理中使用

3. synchronized和ReentrantLock的区别?

我记得校招的时候,这道面试题出现的频率还是挺高的~可以从锁的实现、功能特点、性能等几个维度去回答这个问题,

  • 「锁的实现:」 synchronized是Java语言的关键字,基于JVM实现。而ReentrantLock是基于JDK的API层面实现的(一般是lock()和unlock()方法配合try/finally 语句块来完成。)

  • 「性能:」 在JDK1.6锁优化以前,synchronized的性能比ReenTrantLock差很多。但是JDK6开始,增加了适应性自旋、锁消除等,两者性能就差不多了。

  • 「功能特点:」 ReentrantLock 比 synchronized 增加了一些高级功能,如等待可中断、可实现公平锁、可实现选择性通知。

  • ReentrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制。

  • ReentrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。

  • synchronized与wait()和notify()/notifyAll()方法结合实现等待/通知机制,ReentrantLock类借助Condition接口与newCondition()方法实现。

  • ReentrantLock需要手工声明来加锁和释放锁,一般跟finally配合释放锁。而synchronized不用手动释放锁。

4. 说说CountDownLatch与CyclicBarrier区别

  • CountDownLatch:一个或者多个线程,等待其他多个线程完成某件事情之后才能执行;

  • CyclicBarrier:多个线程互相等待,直到到达同一个同步点,再继续一起执行。

举个例子吧:

  • CountDownLatch:假设老师跟同学约定周末在公园门口集合,等人齐了再发门票。那么,发门票(这个主线程),需要等各位同学都到齐(多个其他线程都完成),才能执行。

  • CyclicBarrier:多名短跑运动员要开始田径比赛,只有等所有运动员准备好,裁判才会鸣枪开始,这时候所有的运动员才会疾步如飞。

5. Fork/Join框架的理解

Fork/Join框架是Java7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。

Fork/Join框架需要理解两个点,「分而治之」「工作窃取算法」

「分而治之」

以上Fork/Join框架的定义,就是分而治之思想的体现啦

「工作窃取算法」

把大任务拆分成小任务,放到不同队列执行,交由不同的线程分别执行时。有的线程优先把自己负责的任务执行完了,其他线程还在慢慢悠悠处理自己的任务,这时候为了充分提高效率,就需要工作盗窃算法啦~

工作盗窃算法就是,「某个线程从其他队列中窃取任务进行执行的过程」。一般就是指做得快的线程(盗窃线程)抢慢的线程的任务来做,同时为了减少锁竞争,通常使用双端队列,即快线程和慢线程各在一端。

6. 为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run()方法?

看看Thread的start方法说明哈~

    /*** Causes this thread to begin execution; the Java Virtual Machine* calls the <code>run</code> method of this thread.* <p>* The result is that two threads are running concurrently: the* current thread (which returns from the call to the* <code>start</code> method) and the other thread (which executes its* <code>run</code> method).* <p>* It is never legal to start a thread more than once.* In particular, a thread may not be restarted once it has completed* execution.** @exception  IllegalThreadStateException  if the thread was already*               started.* @see        #run()* @see        #stop()*/public synchronized void start() {......}

JVM执行start方法,会另起一条线程执行thread的run方法,这才起到多线程的效果~ 「为什么我们不能直接调用run()方法?」如果直接调用Thread的run()方法,其方法还是运行在主线程中,没有起到多线程效果。

7. CAS?CAS 有什么缺陷,如何解决?

CAS,Compare and Swap,比较并交换;

CAS 涉及3个操作数,内存地址值V,预期原值A,新值B;如果内存位置的值V与预期原A值相匹配,就更新为新值B,否则不更新

CAS有什么缺陷?

「ABA 问题」

并发环境下,假设初始条件是A,去修改数据时,发现是A就会执行修改。但是看到的虽然是A,中间可能发生了A变B,B又变回A的情况。此时A已经非彼A,数据即使成功修改,也可能有问题。

可以通过AtomicStampedReference「解决ABA问题」,它,一个带有标记的原子引用类,通过控制变量值的版本来保证CAS的正确性。

「循环时间长开销」

自旋CAS,如果一直循环执行,一直不成功,会给CPU带来非常大的执行开销。

很多时候,CAS思想体现,是有个自旋次数的,就是为了避开这个耗时问题~

「只能保证一个变量的原子操作。」

CAS 保证的是对一个变量执行操作的原子性,如果对多个变量操作时,CAS 目前无法直接保证操作的原子性的。

可以通过这两个方式解决这个问题:

  • 使用互斥锁来保证原子性;

  • 将多个变量封装成对象,通过AtomicReference来保证原子性。

有兴趣的朋友可以看看我之前的这篇实战文章哈~CAS乐观锁解决并发问题的一次实践[2]

9. 如何保证多线程下i++ 结果正确?

  • 使用循环CAS,实现i++原子操作

  • 使用锁机制,实现i++原子操作

  • 使用synchronized,实现i++原子操作

没有代码demo,感觉是没有灵魂的~ 如下:

/***  @Author 捡田螺的小男孩*/
public class AtomicIntegerTest {private static AtomicInteger atomicInteger = new AtomicInteger(0);public static void main(String[] args) throws InterruptedException {testIAdd();}private static void testIAdd() throws InterruptedException {//创建线程池ExecutorService executorService = Executors.newFixedThreadPool(2);for (int i = 0; i < 1000; i++) {executorService.execute(() -> {for (int j = 0; j < 2; j++) {//自增并返回当前值int andIncrement = atomicInteger.incrementAndGet();System.out.println("线程:" + Thread.currentThread().getName() + " count=" + andIncrement);}});}executorService.shutdown();Thread.sleep(100);System.out.println("最终结果是 :" + atomicInteger.get());}}

运行结果:

...
线程:pool-1-thread-1 count=1997
线程:pool-1-thread-1 count=1998
线程:pool-1-thread-1 count=1999
线程:pool-1-thread-2 count=315
线程:pool-1-thread-2 count=2000
最终结果是 :2000

10. 如何检测死锁?怎么预防死锁?死锁四个必要条件

死锁是指多个线程因竞争资源而造成的一种互相等待的僵局。如图感受一下:「死锁的四个必要条件:」

  • 互斥:一次只有一个进程可以使用一个资源。其他进程不能访问已分配给其他进程的资源。

  • 占有且等待:当一个进程在等待分配得到其他资源时,其继续占有已分配得到的资源。

  • 非抢占:不能强行抢占进程中已占有的资源。

  • 循环等待:存在一个封闭的进程链,使得每个资源至少占有此链中下一个进程所需要的一个资源。

「如何预防死锁?」

  • 加锁顺序(线程按顺序办事)

  • 加锁时限 (线程请求所加上权限,超时就放弃,同时释放自己占有的锁)

  • 死锁检测

参考与感谢

牛顿说,我之所以看得远,是因为我站在巨人的肩膀上~ 谢谢以下各位前辈哈~

  • 面试必问的CAS,你懂了吗?[3]

  • Java多线程:死锁[4]

  • ReenTrantLock可重入锁(和synchronized的区别)总结[5]

  • 聊聊并发(八)——Fork/Join 框架介绍[6]

Reference

[1]

Synchronized解析——如果你愿意一层一层剥开我的心: https://juejin.im/post/5d5374076fb9a06ac76da894#comment

[2]

CAS乐观锁解决并发问题的一次实践: https://juejin.im/post/5d0616ade51d457756536791

[3]

面试必问的CAS,你懂了吗?: https://blog.csdn.net/v123411739/article/details/79561458

[4]

Java多线程:死锁: https://www.cnblogs.com/xiaoxi/p/8311034.html

[5]

ReenTrantLock可重入锁(和synchronized的区别)总结: https://blog.csdn.net/qq838642798/article/details/65441415

[6]

聊聊并发(八)——Fork/Join 框架介绍: https://www.infoq.cn/article/fork-join-introduction

往期推荐

被问哭了,一位小姐姐的阿里面经!(附部分答案)


不要一把梭了,这才是SQL优化的正确姿势!|原创干货


关注下方二维码,每一天都有干货!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/545970.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript中的嵌套事件处理(在鼠标移动事件上)

Multiple event handling is the secret ingredient of dynamic WebPages seen now-a-days. 多重事件处理是当今动态网页的秘密组成部分。 Now, let’s get started... 现在&#xff0c;让我们开始吧... Example Code 范例程式码 <html lang"en"><head&…

皮尔逊相关性_皮尔逊的相关性及其在机器学习中的意义

皮尔逊相关性Today we would be using a statistical concept i.e. Pearsons correlation to help us understand the relationships between the feature values (independent values) and the target value (dependent value or the value to be predicted ) which will furt…

磊哥最近面试了好多人,聊聊我的感受!(附面试知识点)

这是我的第 84 篇原创文章作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;一些读者可能知道&#xff0c;磊哥前段时间又回来上班了&#xff0c;详见《磊哥又滚回职场了...》…

M4 宏处理器

2019独角兽企业重金招聘Python工程师标准>>> M4 宏处理器 Brian W. KernighanDennis M. Ritchie Bell LaboratoriesMurray Hill, New Jersey 07974 翻译&#xff1a;寒蝉退士 译者声明&#xff1a;译者对译文不做任何担保&#xff0c;译者对译文不拥有任何权利并且不…

绝了,几款主流的 JSON 库性能对比!

本篇通过JMH&#xff08;Oracle官方测试框架&#xff09;来测试一下Java中几种常见的JSON解析库的性能。每次都在网上看到别人说什么某某库性能是如何如何的好&#xff0c;碾压其他的库。但是百闻不如一见&#xff0c;只有自己亲手测试过的才是最值得相信的。JSON不管是在Web开…

DWZ使用笔记

DWZ使用笔记 一、前言 在近期的一个项目中&#xff0c;引入了DWZ这个富client框架&#xff0c;算是一次尝试吧。期间也遇到不少问题&#xff0c;总算一一攻克了。特以此文记之。本人用的是dwz-ria-1.4.5Asp.net webform&#xff0c;写这篇笔记时最新版本号已经是1.4.6了。DWZ官…

阿里的简历多久可以投递一次?次数多了有没有影响?可以同时进行吗?

最近&#xff0c;无论是读者群&#xff0c;还是公众号后台&#xff0c;很多人都比较关注以下几个问题&#xff1a;阿里的简历是半年只能投递一次吗&#xff1f;阿里的面试可以多个部门同时进行吗&#xff1f;面试没过&#xff0c;又被系统捞起来了&#xff0c;我该怎么办&#…

记一次蚂蚁金服面试被虐经历

本文来自作者投稿&#xff0c;原作者&#xff1a;yes面试前的小姐姐来说说前不久蚂蚁金服一面的情况。说来也是巧合&#xff0c;当时在群里有位蚂蚁金服的小姐姐发了个内推&#xff0c;看了下JD感觉可以试试于是就私聊了小姐姐发简历内推了。我16年也就是大三上就开始实习了&am…

用python + openpyxl处理excel(07+)文档 + 一些中文处理的技巧

2019独角兽企业重金招聘Python工程师标准>>> 寻觅工具 确定任务之后第一步就是找个趁手的库来干活。 Python Excel上列出了xlrd、xlwt、xlutils这几个包&#xff0c;但是 它们都比较老&#xff0c;xlwt甚至不支持07版以后的excel它们的文档不太友好&#xff0c;都可…

Spring Boot 2.3.3 正式发布!

Spring Boot 2.3.3 稳定版已发布&#xff0c;可从 repo.spring.io 和 Maven Central 获取。<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.3.RELEASE</versio…

木板最优切割利润最大_最多进行K笔交易的股票最大买卖利润

木板最优切割利润最大This is a very popular interview problem to find maximum profit in stock buying and selling with at most K transactions. This problem has been featured in the interview rounds of Amazon. 这是一个非常受欢迎的面试问题&#xff0c;目的是在最…

[数据库]Oracle和mysql中的分页总结

Mysql中的分页物理分页•在sql查询时&#xff0c;从数据库只检索分页需要的数据•通常不同的数据库有着不同的物理分页语句•mysql物理分页&#xff0c;采用limit关键字•例如&#xff1a;检索11-20条 select * from user limit 10,10 ;* 每次只查询10条记录.当点击下一页的时候…

List 集合去重的 3 种方法

问题由来在实际开发的时候&#xff0c;我们经常会碰到这么一个困难&#xff1a;一个集合容器里面有很多重复的对象&#xff0c;里面的对象没有主键&#xff0c;但是根据业务的需求&#xff0c;实际上我们需要根据条件筛选出没有重复的对象。比较暴力的方法&#xff0c;就是根据…

C语言入门——排序

排序的方法有很多种比较常见的便为&#xff1a;冒泡排序、选择排序、插入排序、快速排序。 今天我们就围绕着四种排序来说&#xff0c;如果有兴趣的话可以去查找一下其他排序。 在排序这方面我们主要讨论&#xff1a; 稳定&#xff1a;如果a原本在b前面&#xff0c;而ab&…

【转】eclipse技巧1

2019独角兽企业重金招聘Python工程师标准>>> 俗话说的好啊&#xff0c;“工于利启事&#xff0c;必先善其器”&#xff0c;如果说你的编程功底是一个枪法的话&#xff0c;那么强大的eclipse就是android战士们最好的武器。 这里&#xff0c;我们来总结eclipse的使用技…

定时任务最简单的3种实现方法(超好用)

这是我的第 86 篇原创文章作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;定时任务在实际的开发中特别常见&#xff0c;比如电商平台 30 分钟后自动取消未支付的订单&#x…

C语言入门基础——Brute-Force算法

Brute-Force算法的基本思想是&#xff1a; 1) 从目标串s 的第一个字符起和模式串t的第一个字符进行比较&#xff0c;若相等&#xff0c;则继续逐个比较后续字符&#xff0c;否则从串s 的第二个字符起再重新和串t进行比较。 2) 依此类推&#xff0c;直至串t 中的每个字符依次和…

为什么劝你放弃Maven?看看Gradle的这些优点就知道了

相信使用Java的同学都用过Maven&#xff0c;这是一个非常经典好用的项目构建工具。但是如果你经常使用Maven&#xff0c;可能会发现Maven有一些地方用的让人不太舒服&#xff1a;Maven的配置文件是XML格式的&#xff0c;假如你的项目依赖的包比较多&#xff0c;那么XML文件就会…

css中的换行符_如何使用CSS防止项目列表中的换行符?

css中的换行符Introduction: 介绍&#xff1a; Dealing with various items in CSS sometimes pose very different problems. The problem could be anything, it could be related to positioning, arrangement, and whatnot, therefore all such kinds of problems require…

Java中的一些坑,汇总篇(2万字)

Photo Drew Farwell 文 | 常意1.前言