Java岗大厂面试百日冲刺 - 日积月累,每日三题Day10 —— 并发编程1
Posted _陈哈哈
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java岗大厂面试百日冲刺 - 日积月累,每日三题Day10 —— 并发编程1相关的知识,希望对你有一定的参考价值。
大家好,我是陈哈哈,北漂五年。认识我的朋友们知道,我是非科班出身,半路出家,大学也很差!这种背景来北漂,你都不知道你会经历什么🙃🙃。
不敢苟同,相信大家和我一样,都有一个大厂梦
,作为一名资深Java选手,深知面试重要性,接下来我准备用100天时间,基于Java岗面试中的高频面试题,以每日3题
的形式,带你过一遍热门面试题及恰如其分的解答。当然,我不会太深入,因为我怕记不住!!
因此,不足的地方希望各位在评论区补充疑惑、见解以及面试中遇到的奇葩问法
,希望这100天能够让我们有质的飞越,一起冲进大厂!!,让我们一起学(juan)起来!!!
今天群里老铁投稿的照片都很赞!都很有feeling!以后都会注明作者,不能白嫖~~~
@ K🐳
车票
本栏目Java开发岗高频面试题主要出自以下各技术栈:Java基础知识
、集合容器
、并发编程
、JVM
、Spring全家桶
、MyBatis等ORMapping框架
、mysql数据库
、Redis缓存
、RabbitMQ消息队列
、Linux操作技巧
等。
面试题1:简单说下你对线程和进程的理解?
正经回答:
进程
- 一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,比如在Windows系统中,一个运行的xx.exe就是一个进程。
线程
- 进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。
深入追问:
追问1:那进程和线程有哪些区别呢?
想起了某乎上的经典回答:
做个简单的比喻:进程 → 火车,线程 → 车厢;线程在进程下行进(单纯的车厢无法运行)
- 一个进程可以包含多个线程(一辆火车可以有多个车厢)
- 不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
- 同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
- 进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
- 进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)
- 进程可以拓展到多机,线程最多扩展到多核CPU,而不能扩展到多机(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)
- 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-“互斥锁”
- 进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”
课间休息,又来秀一下来自咱们群里同学的搬砖工地,坐标:广州 小蛮腰 广州塔码头。
@🌞
面试题2:守护线程和用户线程的区别?
正经回答:
用户 (User) 线程:
- 运行在前台,执行具体的任务,如程序的主线程、连接网络的子线程等都是用户线程
守护 (Daemon) 线程:
- 运行在后台,为其他前台线程(非守护线程)服务。当
所有用户线程都结束运行
时,守护线程会随 JVM 一起结束工作.
可见,守护线程是依赖于用户线程,当所有用户线程都退出了,守护线程也就会退出,典型的守护线程如垃圾回收线程。
而用户线程是独立存在的,不会因为其他用户线程退出而退出。
注意事项:
- setDaemon(true)必须在
start()
方法前执行,否则会抛出IllegalThreadStateException
异常 - 在守护线程中产生的新线程也是守护线程
- 不是所有的任务都可以分配给守护线程来执行,比如读写操作或者计算逻辑
- 守护 (Daemon) 线程中不能依靠 finally 块的内容来确保执行关闭或清理资源的逻辑。因为我们上面也说过了一旦所有用户线程都结束运行,守护线程会随 JVM 一起结束工作,所以守护 (Daemon) 线程中的 finally 语句块可能无法被执行。
课间休息,又双来秀一下来自咱们群里同学的搬砖工地,坐标:???
@晓海williams
。
面试题3:什么是线程死锁?
正经回答:
死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程(线程)称为死锁进程(线程)。
例如,在某个计算机系统中只有一台打印机和一台输入 设备,进程A正占用输入设备,同时又提出使用打印机的请求,但此时打印机正被进程B 所占用,而B在未释放打印机之前,又提出请求使用正被A占用着的输入设备。这样两个进程相互无休止地等待下去,均无法继续执行,此时两个进程陷入死锁状态。
多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。
下面是一个死锁示例代码:
// 示例来自《并发编程之美》
public class DeadLockDemo {
private static Object resource1 = new Object();//资源 1
private static Object resource2 = new Object();//资源 2
public static void main(String[] args) {
new Thread(() -> {
synchronized (resource1) {
System.out.println(Thread.currentThread() + "get resource1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "waiting get resource2");
synchronized (resource2) {
System.out.println(Thread.currentThread() + "get resource2");
}
}
}, "线程 1").start();
new Thread(() -> {
synchronized (resource2) {
System.out.println(Thread.currentThread() + "get resource2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "waiting get resource1");
synchronized (resource1) {
System.out.println(Thread.currentThread() + "get resource1");
}
}
}, "线程 2").start();
}
}
打印输出:
Thread[线程 1,5,main]get resource1
Thread[线程 2,5,main]get resource2
Thread[线程 1,5,main]waiting get resource2
Thread[线程 2,5,main]waiting get resource1
线程 A 通过 synchronized (resource1) 获得 resource1 的监视器锁,然后通过Thread.sleep(1000);让线程 A 休眠 1s 为的是让线程 B 得到CPU执行权,从而获取到 resource2 的监视器锁。
线程 A 和线程 B 休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。上面的例子符合产生死锁的四个必要条件。
深入追问:
追问1:形成死锁的四个必要条件是什么?
互斥
: 某种资源一次只允许一个进程访问,即该资源一旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束。占有且等待
: 一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源。不可抢占
: 别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来。循环等待
: 存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源。
当以上四个条件均满足,必然会造成死锁,相反,而只要上述条件之一不满足,就不会发生死锁。
发生死锁的进程无法进行下去,它们所持有的资源也无法释放。这样会导致CPU的吞吐量下降。所以死锁情况是会浪费系统资源和影响计算机的使用性能的。
追问2:我们该如何避免死锁?
死锁避免的基本思想:系统对进程发出每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,如果分配后系统可能发生死锁,则不予分配,否则予以分配。这是一种保证系统不进入死锁状态的动态策略。
理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和解除死锁。只要打破四个必要条件之一就能有效预防死锁的发生:
打破互斥条件
:改造独占性资源为虚拟资源,大部分资源已无法改造。打破不可抢占条件
:当一进程占有一独占性资源后又申请一独占性资源而无法满足,则退出原占有的资源。打破占有且等待条件
:采用资源预先分配策略,即进程运行前申请全部资源,满足则运行,不然就等待,这样就不会占有且申请。打破循环等待条件
:实现资源有序分配策略,对所有设备实现分类编号,所有进程只能采用按序号递增的形式申请资源。
追问3:死锁避免和死锁预防有啥不同?
死锁预防是设法至少破坏产生死锁的四个必要条件之一,严格的防止死锁的出现;而死锁避免则不那么严格的限制产生死锁的必要条件的存在,因为即使死锁的必要条件存在,也不一定发生死锁。死锁避免是在系统运行过程中注意避免死锁的最终发生。
最后,又双叒来秀一下来自咱们群里晚上九点下班的美女同学,坐标:???
@雾漫.
每日小结
今天我们复习了面试中常考的多线程相关的三方面问题,你做到心中有数了么?对了,如果你的朋友也在准备面试,请将这个系列扔给他,如果他认真对待,肯定会感谢你的!!
好了,今天就到这里,学废了的同学,记得在评论区留言:打卡。
,给同学们以激励。
以上是关于Java岗大厂面试百日冲刺 - 日积月累,每日三题Day10 —— 并发编程1的主要内容,如果未能解决你的问题,请参考以下文章
Java岗大厂面试百日冲刺 - 日积月累,每日三题Day02——Java高级篇
Java岗大厂面试百日冲刺Day47— 并发编程4(日积月累,每日三题)
Java岗大厂面试百日冲刺 - 日积月累,每日三题Day51—— tomcat
Java岗大厂面试百日冲刺 - 日积月累,每日三题Day23—— 算法1