大厂面试题-Java并发编程基础篇(二)

news2024/9/25 13:23:56

目录

一、wait和notify这个为什么要在synchronized代码块中?

二、ThreadLocal是什么?它的实现原理呢?

三、基于数组的阻塞队列ArrayBlockingQueue原理

四、怎么理解线程安全?

五、请简述一下伪共享的概念以及如何避免

六、什么是可重入,什么是可重入锁?它用来解决什么问题?

七、ReentrantLock的实现原理

八、简述一下你对线程池的理解?

九、如何中断一个正在运行的线程?

十、为什么引入偏向锁、轻量级锁,介绍下升级流程


一、wait和notify这个为什么要在synchronized代码块中?

1.   waitnotify用来实现线程之间的协调,wait表示让线程进入到阻塞状态,notify表示让阻塞的线程唤醒。

2.   waitnotify必然是成对出现的,如果一个线程被wait()方法阻塞,那么必然需要另外一个线程通过notify()方法来唤醒这个被阻塞的线程,从而实现多线程之间的通信。

3.     (如图)在多线程里面,要实现多个线程之间的通信,除了管道流以外,只能通过共享量的方法来实现,也就是线程t1修改共享变量s,线程t2获取修改后的共享变量s,从而完成数据通信。

但是多线程本身具有并行执行的特性,也就是在同一时刻,多个线程可以同时执行。在这种情况下,线程t2在访问共享变量s之前,必须要知道线程t1已经修改过了共享变s,否则就需要等待。

同时,线程t1修改过了共享变量S之后,还需要通知在等待中的线程t2。

所以要在这种特性下要去实现线程之间的通信,就必须要有一个竞争条件控制线程在什条件下等待,什么条件下唤醒。

4.   而Synchronized同步关键字就可以实现这样一个互斥条件,也就是在通过共享变量来实现多个线程通信的场景里面,参与通信的线程必须要竞争到这个共享变量的锁资源,才有资格对共享变量做修改,修改完成后就释放锁,那么其他的线程就可以再次来竞争同一个共享变量的锁来获取修改后的数据,从而完成线程之前的通。

5.   所以这也是为什么wait/notify需要放在Synchronized同步代码块中的原因,有Synchronized同步锁,就可以实现对多个通信线程之间的互斥,实现条件等待和条件唤醒。

6.   另外,为了避免wait/notify的错误使用,jdk强制要求把wait/notify写在同步代码块里面,否则会抛出IllegalMonitorStateException

7.   最后,基于wait/notify的特性,非常适合实现生产者消费者的模型,比如说用wait/notify来实现连接池就绪前的等待与就绪后的唤醒。

二、ThreadLocal是什么?它的实现原理呢?

从三个方面来回答

  1. 1.   ThreadLocal是一种线程隔离机制,它提供了多线程环境下对于共享变量访问的安全性
  2. 2.   在多线程访问共享变量的场景中(出现下面第一个图),一般的解决办法是对共享变量加锁(出现下面第二个图),从而保证在同一时刻只有一个线程能够对共享变量进行更新,并且基于Happens-Before规则里面的监视器锁规则,又保证了数据修改后对其他线程的可见性。

3.但是加锁会带来性能的下降,所以ThreadLocal用了一种空间换时间的设计思想,也就是说在每个线程里面,都有一个容器来存储共享变量的副本,然后每个线程只对自己的变量副本来做更新操作,这样既解决了线程安全问题,又避免了多线程竞争加锁的开销。

4.ThreadLocal的具体实现原理是,在Thread类里面有一个成员变量ThreadLocalMap,它专门来存储当前线程的共享变量副本,后续这个线程对于共享变量的操作,都是从这个ThreadLocalMap里面进行变更,不会影响全局共享变量的值。

三、基于数组的阻塞队列ArrayBlockingQueue原理

1.     (如图)阻塞队列(BlockingQueue)是在队列的基础上增加了两个附加操作

a. 在队列为空的时候,获取元素的线程会等待队列变为非空。

b. 当队列满时,存储元素的线程会等待队列可用。

2.   由于阻塞队列的特性,可以非常容易实现生产者消费者模型,也就是生产者只需要关心数据的生产,消费者只需要关注数据的消费,所以如果队列满了,生产者就等,同样,队列空了,消费者也需要等待。

3.   要实现这样的一个阻塞队列,需要用到两个关键的技术,队列元素的存储、以及线程阻塞和唤醒。

4.   而ArrayBlockingQueue是基于数组结构的阻塞队列,也就是队列元素是存储在一个数组结构里面,并且由于数组有长度限制,为了达到循环生产和循环消费的目的,ArrayBlockingQueue用到了循环组。

5.   而线程的阻塞和唤醒,用到了J.U.C包里面的ReentrantLock和Condition。Condition相当于wait/notifyJUC包里面的实现。

四、怎么理解线程安全?

简单来说,在多个线程访问某个方法或者对象的时候,不管通过任何的方式调用以及线程如何去交替执行。

在程序中不做任何同步干预操作的情况下,这个方法或者对象的执行/修改都能按照预的结果来反馈,那么这个类就是线程安全的。

实际上,线程安全问题的具体表现体现在三个方面原子性、有序性、可见

原子性指当一个线程执行一系列程序指令操作的时候,它应该是不可中断的,因为一旦出现中断,站在多线程的视角来看,这一系列的程序指令会出现前后执行结果不一致的问题。

这个和数据库里面的原子性是一样的,简单来说就是一段程序只能由一个线程完整的执行完成,而不能存在多个线程干扰。

(如图)CPU的上下文切换,是导致原子性问题的核心,而JVM里面提供了Synchronized关键字来解决原子性问题。

可见性说在多线程环境下,由于读和写是发生在不同的线程里面,有可能出现某个线程对共享变量的修改,对其他线程不是实时可见的。

导致可见性问题的原因有很多,比如CPU的高速缓存、CPU的指令重排序、编译器的指令重排序。

有序性指的是程序编写的指令顺序和最终CPU运行的指令顺序可能出现不一致的现象,这种现象也可以称为指令重排序,所以有序性也会导致可见性问题。

可见性和有序性可以通过JVM里面提供了一个Volatile关键字来解决。

导致有序性、原子性、可见性问题的本质,是计算机工程师为了最大化提升CPU用率导致的。比如为了提升CPU利用率,设计了三级缓存、设计了StoreBuffer、设计了缓存行这种预读机制、在操作系统里面,设计了线程模型、在编译器里面,设计了编译器的深度优化机制。

五、请简述一下伪共享的概念以及如何避免

对于这个题,要从几个方面来回答

首先计算机工程师为了提高CPU的利用率,平衡CPU和内存之间的速度差异,在CPU里面设计了三级缓存。

CPU在向内存发起IO操作的时候,一次性会读取64个字节的数据作为一个缓存行,缓存到CPU的高速缓存里面。

Java一个long类型是8个字节,意味着一个缓存行可以存储8个long类型的变

这个设计是基于空间局部性原理来实现的,也就是说,如果一个存储器的位置被引用,那么将来它附近的位置也会被引用。

缓存行的设计对于CPU来说,可以有效的减少和内存的交互次数,从而避免了CPUIO等待,以提升CPU的利用率。

正是因为这种缓存行的设计,导致如果多个线程修改同一个缓存行里面的多个独立变量的时候,基于缓存一致性协议,就会无意中影响了彼此的性能,这就是伪共享的问题

(如图)像这样一种情况,CPU0上运行的线程想要更新变量X、CPU1上的线程想要更新变量Y,而X/Y/Z都在同一个缓存行里面。

每个线程都需要去竞争缓存行的所有权对变量做更新,基于缓存一致性协议

一旦运行在某个CPU上的线程获了所有权并执行了修改,就会导致其他CPU中的缓存行失效。

就是伪共享问题的原理。

因为伪共享会问题导致缓存锁的竞争,所以在并发场景中的程序执行效率一定会收到较大的影响。

这个问题的解决办法有两个:

1.  使用对齐填充,因为一个缓存行大小是64个字节,如果读取的目标数据小于64个字节,可以增加一些无意义的成员变量来填充

2.  在Java8里面,提供了@Contented注解,它也是通过缓存行填充来解决伪共享问题的,被@Contented注解声明的类或者字段,会被加载到独立的缓存行上。

六、什么是可重入,什么是可重入锁?它用来解决什么问题?

可重入多线程并发编程里面一个比较重要的概念,

简单来说,就是在运行的某个函数或者代码,因为抢占资源或者中断等原因导致函数或者代码的运行中断,等待中断程序执行结束后,重新进入到这个函数或者代码中运行,并且运行结果不会受到影响,那么这个函数或者代码就是可重入的。

(如图)而可重入锁,简单来说就是一个线程如果抢占到了互斥锁资源,在锁释放之前再去竞争同一把的时候,不需要等待,只需要记录重入次数。

在多线程并发编程里面,绝大部分锁都是可重入的,比如Synchronized、ReentrantLock等,但是也有不支持重入的锁,比如JDK8里面提供的读写锁StampedLock。

锁的可重入性,主要解决的问题是避免线程死锁的问题。

因为一个已经获得同步锁X的线程,在释放锁X之前再去竞争锁X的时候,相当于会出现自己要等待自己释放锁,这很显然是无法成立的。

七、ReentrantLock的实现原理

关于这个问题从这几个方面来回答

    1、什么是ReentrantLock

    2、ReentrantLock的特性

    3、ReentrantLock的实现原理

首先,ReentrantLock是一种可入的排它锁,主要用来解决多线程对共享资源竞争的题。

它的核心特性有几个:

1.   它支持可重入,也就是获得锁的线程在释放锁之前再次去竞争同一把锁的时候,不需要加锁就可以直接访问。

2.   它支持公平和非公平特

3.   它提供了阻塞竞争锁和非阻塞竞争锁的两种方法,分别是lock()和tryLock()。

(如图)然后,ReentrantLock的底层实现有几个非常关键的技术。

4.   锁的竞争,ReentrantLock是通过互斥变量,使用CAS机制来实现的。

5.   没有竞争到锁的线程,使用了AbstractQueuedSynchronizer这样一个队列同步器来存储,底层是通过双向链表来实现的。当锁被释放之后,会从AQS队列里面头部唤醒下一个等待锁的线程。

6.   公平和非公平的特性,主要是体现在竞争锁的时候,是否需要判断AQS队列存在等待中的线程。

7.   最后,关于锁的重入特性,在AQS里面有一个成员变量来保存当前获得锁的线程,当同一个线程下次再来竞争锁的时候,就不会去走锁竞争的逻辑,而是直接增加重入次数。

八、简述一下你对线程池的理解?

关于这个问题,从几个方面来回答

首先,线程池本质上是一种池化技术,而池化技术是一种资源复用的思想,比较常见的有连接池、内存池、对象池。

线程池里面复用的是线程资源,它的核心设计目标,有两个:

1.   减少线程的频繁创建和销毁带来的性能开销,因为线程创建会涉及到CPU上下文换、内存分配等工作。

2.   线程池本身会有参数来控制线程创建的数量,这样就可以避免无休止的创建线程带来的资源利用率过高的问题,起到了资源保护的作用。

其次,单说一下线程池里面的线程复用技术。因为线程本身并不是一个受控的技术,也就是说线程的生命周期时由任务运行的状态决定的,无法人为控制。

(图片)所以为了实现线程的复用,线程池里面用到了阻塞队列,简单来说就是线程池里面的工作线程处于一直运行状态,它会从阻塞队列中去获取待执行的任务,一旦队列空了那这个工作线程就会被阻塞,直到下次有新的任务进来。

也就是说工作线程是根据任务的情况实现阻塞和唤醒,从而达到线程复用的目的。最后,线程池里面的资源限制,是通过几个关键参数来控制的,分别是核心线程数、最大线程数。

核心线程数表示默认长期存在的工作线程,而最大线程数是根据任务的情况动态创建的线程,主要是提高阻塞队列中任务的理效率。

九、如何中断一个正在运行的线程?

关于这个问题,从几个方面来回答

(如图)首先,线程是系统级别的概念,在Java里面实现的线程,最终的执行和调度都是由操作系统来决定的,JVM只是对操作系统层面的线程做了一层包装而已。

以我们在Java里面通过start方法启动一个线程的时候,只是告诉操作系统这个线程可以被执行,但是最终交给CPU来执行是操作系统的调度算法来决定的。

,理论上来说,要在Java层面去中断一个正在运行的线程,只能像类似于Linux里面kill命令结束进程的方式一样,强制终止。

所以,Java Thread里面提供了一个stop方法可以强行终止,但是这种方式是不安全因为有可能线程的任务还没有,导致出现运行结果不正确的问题。

要想安全的中断一个正在运行的线程,只能在线程内部埋下一个钩子,外部程序通过这个钩子来触发线程的中断命令。

(如图)因此,在Java Thread里面提供了一个interrupt()方法,这个方法配合isInterrupted()方法使用,就可以实现安全的中断机制。

这种实现方法并不是强制中断,而是告诉正在运行的线程,你可以停止了,不过是否要中断,取决于正在运行的线程,所以它能够保证线程运行结果的安全性。

十、为什么引入偏向锁、轻量级锁,介绍下升级流程

比如共享锁、排它锁、偏向锁、轻量级锁、自旋锁、重量级锁、间隙锁、临键锁、意向锁、读写锁、乐观锁、悲观锁、表锁、行锁。

1.Synchronizedjdk1.6版本之前,是通过重量级锁的方式来实现线程之间锁的竞

之所以称它为重量级锁,是因为它的底层底层依赖操作系统的MutexLock来实现互斥功能

(图)Mutex是系统方法,由于权限隔离的关系,应用程序调用系统方法时需要切到内核态来执行。

这里涉到用户态向内核态的切换,这个切换会带来性能的损耗。

2.在jdk1.6版本中,synchronized增加了锁升级的机制,来平衡数据安全性和性能。简单来,就是线程去访问synchronized同步代码块的时候,synchronized根线程竞争情况,会先尝试在不加重量级锁的情况下去保证线程安全性。所以引入了偏向锁和轻量级锁的机制。

偏向锁,就是直接把当前锁偏向于某线程,简单来说就是通过CAS修改偏向锁标记,这种锁适合同一个线程多次去申请同一个锁资源并且没有其他线程竞争的场景。

轻量级锁也可以称为自旋锁,基于自适应自旋的机制,通过多次自旋重试去竞争锁。自旋锁优点在于它避免避免了用户态到内核态的切换带来的性能开销。

3.(如图)Synchronized入了锁升级的机制之后,如果有线程去竞争锁:

首先,synchronized会尝试使用偏向锁的方式去竞争锁资源,如果能够竞争到偏向锁,表示加锁成功直接返回。如果竞争锁失败,说明当前锁已经偏向了其他线程。

需要将锁升级到轻量级锁,在轻量级锁状态下,竞争锁的线程根据自适应自旋次数去尝试抢占锁资源,如果在轻量级锁状态下还是没有竞争到锁,就只能升级到重量级锁,在重量级锁状态下,没有竞争到锁的线程就会被阻塞,线程状态是Blocked。

于锁等待状态的线程需要等待获得锁的线程来触发唤醒。

总的说,Synchronized的锁升级的设计思想,本质上是一种性能和安全性的平衡,也就是如何在不加锁的情况下能够保证线程安全性。

这种思想在编程领域比较常见,比如Mysql里面的MVCC使用版本链的方式来解决多个并行事务的竞争问题。

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

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

相关文章

【Qt之控件QKeySequenceEdit】分析及使用

描述 QKeySequenceEdit小部件允许输入一个QKeySequence。 该小部件允许用户选择一个QKeySequence,通常用作快捷键。当小部件获取焦点时,录制将开始,并在用户释放最后一个键后的一秒钟结束。 用户可以使用输入键盘来输入键序列。通过调用get…

迁移学习 - 微调

什么是与训练和微调? 你需要搭建一个网络模型来完成一个特定的图像分类的任务。首先,你需要随机初始化参数,然后开始训练网络,不断调整参数,直到网络的损失越来越小。在训练的过程中,一开始初始化的参数会…

matlab创建矩阵、理解三维矩阵

1.创建矩阵 全0矩阵:a zeros(2,3,4) 全1矩阵:a ones(2,3,4) !和python不一样的地方!此处相当于创建了4页2行3列的矩阵,而在python里是2页3行4列。 对第1页的第2行第3列元素进行修改:

【中国知名企业高管团队】系列49:VIVO

今天为您介绍蓝绿两厂的蓝厂——VIVO。这两家公司同源于步步高,两家公司除了名字都是四个字以外,其他方面也实在是太像了,就连核心价值观的前两个词都一样:本分、用户导向。 一、VIVO公司简介 和OPPO一样,VIVO也来源…

[微信小程序踩坑]微信小程序editor富文本组件渲染字符串时,内部图片超出大小导致无法正常渲染或回显(数据传输长度为 3458 KB,存在有性能问题!)

坑一&#xff1a;回显问题 富文本组件&#xff1a; <editor id"editor" name"{{name}}" style"font-size: 28rpx;color: #C9CDD4" read-only"{{true}}" placeholder"{{placeholder}}" bind:input"onChange11"…

Java实现大学兼职教师管理系统 开源

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容三、界面展示3.1 登录注册3.2 学生教师管理3.3 课程管理模块3.4 授课管理模块3.5 课程考勤模块3.6 课程评价模块3.7 课程成绩模块3.8 可视化图表 四、免责说明 一、摘要 1.1 项目介绍 大学兼职教师管理系统&#xff0c;旨…

汇编运算符和表达式

运算符&#xff1a; 汇编语言由表达式和运算符组成&#xff0c;运算符分为数值运算符和属性运算符。属性运算符面向变量或标号。 数值运算符&#xff1a; 算术运算符&#xff1a; 运算符类型 ✓ ( 正号 ) 、 -( 负号 ) ✓ ( 加 ) 、 -( 减 ) 、 *( 乘 ) 、 /( 除 ) 、 MO…

Linux常用命令——chpasswd命令

在线Linux命令查询工具 chpasswd 批量更新用户口令的工具 补充说明 chpasswd命令是批量更新用户口令的工具&#xff0c;是把一个文件内容重新定向添加到/etc/shadow中。 语法 chpasswd(选项)选项 -e&#xff1a;输入的密码是加密后的密文&#xff1b; -h&#xff1a;显示…

CS224W1.2——图机器学习应用

文章目录 1. 任务分类2. 节点层级任务3. 边层级任务4. 子图层级任务5. 图层级任务 这节我们讲讨论图机器学习的应用。 1. 任务分类 在图机器学习中&#xff0c;我们有不同的任务&#xff1a; 节点层级的任务边层级的任务子图层级任务整张图层级任务&#xff08;图预测&#xf…

应用在温度测量仪领域中的数字温度传感芯片

用于测量温度的仪器。测量仪是测温仪器类型的其中之一。根据所用测温物质的不同和测温范围的不同&#xff0c;有煤油温度计、酒精温度计、水银温度计、气体温度计、电阻温度计、温差电偶温度计、辐射温度计和光测温度计、双金属温度计等。 温度测量仪表按测温方式可分为接触式…

网络协议--TCP连接的建立与终止

18.1 引言 TCP是一个面向连接的协议。无论哪一方向另一方发送数据之前&#xff0c;都必须先在双方之间建立一条连接。本章将详细讨论一个TCP连接是如何建立的以及通信结束后是如何终止的。 这种两端间连接的建立与无连接协议如UDP不同。我们在第11章看到一端使用UDP向另一端发…

selenium (自动化概念 测试环境配置)

什么是自动化测试 自动化测试介绍 自动化测试指软件测试的自动化&#xff0c;在预设状态下运行应用程序或者系统. 预设条件包括正常和异常&#xff0c;最后评估运行结果。   自动化测试&#xff0c;就是将人为驱动的测试行为转化为机器执行的过程。 【机器 代替 人工】 自动化…

1-径向基(RBF)神经网络PID控制器仿真

1、内容简介 略 1-可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 4、参考论文 略

毅速丨哪些金属材料在3D打印中应用最多

金属3D打印作为一种新兴的制造技术已经在很多领域得到广泛应用&#xff0c;目前金属3D打印应用较多的材料有不锈钢、钛合金、铝合金、钴铬合金、镍基合金、模具钢等&#xff0c;其中不锈钢材料的应用最为常见。 这些金属3D打印材料各有其特点和适用场景&#xff0c;可以根据具体…

nodejs+vue宁夏旅游景点客流量数据分析

然后通过引入混沌算法构建了旅游景点客流量预测的学习样本,最后引入数据挖掘技术对旅游景点客流量预测进行建模, 与粒子群算法优化BP神经网络的,支持向量机的旅游景点客流量预测模型的 目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1…

RTE(Runtime Environment)

RTE&#xff08;Runtime Environment&#xff09;是一个运行时环境&#xff0c;在这个环境里&#xff0c;你可以实现的功能是&#xff1a; 作为一个缓冲buffer给应用层和BSW层的接口&#xff08;例如COM&#xff09;用来存储数据&#xff0c;也就是说定义一个全局变量供上层和下…

66 内网安全-域横向批量atschtasksimpacket

目录 演示案例:横向渗透明文传递at&schtasks 案例2-横向渗透明文HASH传递atexec-impacket案例3-横向渗透明文HASH传递批量利用-综合案例5-探针主机域控架构服务操作演示 传递攻击是建立在明文和hash值的一个获取基础上的攻击&#xff0c;也是在内网里面常见协议的攻击&…

【算法】滑动窗口题单——3.不定长滑动窗口(求最短/最小)⭐ 删除最短的子数组使剩余数组有序

文章目录 209. 长度最小的子数组O(n)滑动窗口O(nlogn) 前缀和二分查找 1234. 替换子串得到平衡字符串1574. 删除最短的子数组使剩余数组有序⭐枚举左端点&#xff0c;移动右端点枚举右端点&#xff0c;移动左端点 76. 最小覆盖子串 题单来源&#xff1a;https://leetcode.cn/pr…

SpringBoot | SpringBoot中实现“微信支付“

SpringBoot中实现"微信支付": 1.“微信支付”产品2."微信支付"接入流程3.“微信小程序支付”时序图&#xff1a;3.1 “商家端JSAPI下单” 接口3.2 “微信小程序端调起支付” 接口 4.微信支付准备工作&#xff1a;4.1 获得微信支付平台证书、商户私钥文件4…

博通BCM575系列 RDMA 网卡驱动 bnxt_re 分析(一)

简介 整个BCM系列驱动分成以太网部分(bnxt_en.ko)和RDMA部分(bnxt_re.ko), 两个模块之间通过内核的auxiliary_bus进行管理.我们主要分析下bnxt_re驱动. 代码结构 这个驱动的核心是 qplib_fp.c, 这个文件主要包含了驱动的数据路径, 包括Post Send, Post Recv, Poll CQ流程的实…