当前位置: 首页 > news >正文

面试八股文--并发编程篇

一、线程和进程

1、线程和进程的定义

  • 进程:是资源分配的最小单位,是指计算机中正在运行的一个实例,如你打开了浏览器就是打开了一个进程。
  • 线程:是程序运行的最小单位。一个进程中包含多个线程,他们可以共享进程的进程的资源比如内存空间、文件句柄等。

2、线程和进程的区别

  • 进程是正在运行程序的实例,进程中包含了线程,每个线程执行不同的任务
  • 不同的进程使用不同的内存空间,在当前进程下的所有线程可以共享内存空间
  • 线程更轻量,切换成本一般比进程低

3、创建线程

(1)Q:创建线程的方式有哪些?

  • 继承Thread类
  • 实现runnable接口
  • 实现callable接口
  • 从线程池中创建(项目中使用方式)

(2)Q:runnable和callable的区别?

  • Runnable接口run方法没有返回值;而callable接口call方法有返回值
  • callable接口的call方法允许抛出异常;而runnable接口的run方法的异常只能在内部消化,不能继续网上抛

(3)Q:run()和start()的区别?

  • start()用来启动线程,只能被调用一次
  • run()封装了要被线程执行的代码,可以被调用多次

4、线程的状态

(1)Q:线程包括哪些状态?

新建(NEW)、可运行(RUNNABLE)、阻塞(BLOCKED)、等待(WAITING)、时间等待(TIMED_WAITING)、终止(TERMINATED)

(2)线程之间的状态是如何变化的?

(3)Q:notify()和notifyAll()有什么区别?

  •  notify():随机唤醒一个wait的线程
  • notifyAll():唤醒所有wait线程

(4)Q:wait()方法和sleep()方法有什么区别?

共同点:都是让当前的线程暂时放弃CPU使用权,进入阻塞状态

不同点:

  • 方法归属不同:sleep是静态方法,而wait是Object成员方法,每个对象都有
  • 醒来时机不同:sleep和wait(long)在等待对应毫秒后醒来,但wait()方法如果不唤醒会一直等待
  • 锁特性不同(重点):wait方法调用必须先获取wait对象锁,但sleep不用;wait方法执行后会释放锁,别的线程还能用,但sleep在沉睡期间是不会释放锁的,其他线程也被阻塞。

二、并行和并发

1、并行和并发有什么区别?

在多核CPU下

  • 并发是指同一时间下应对多项事情的能力(微观上是串行的),如多个线程轮流使用1个或多个CPU
  • 并行是指在同一时间下动手做多项事情的能力(也可以说同一时刻),如4核CPU同时执行4个线程

2、Synchronized关键字的底层原理

 Synchronized采用互斥的方式让同一时刻最多只有一个线程能持有对象锁

Synchronized的底层是由monitor实现的,monitor是jvm级别的对象,线程获得锁需要使用对象锁关联monitor,下图展示了monitor的组成

3、死锁

 死锁(Deadlock)描述的是这样一种情况:多个进程/线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于进程/线程被无限期地阻塞,因此程序不可能正常终止。

(1)Q:死锁产生的必要条件?

  • 互斥:资源必须处于非共享模式,即一次只有一个进程可以使用。如果另一进程申请该资源,那么必须等待直到该资源被释放为止。
  • 占有并等待:一个进程至少应该占有一个资源,并等待另一资源,而该资源被其他进程所占有。
  • 非抢占:资源不能被抢占。只能在持有资源的进程完成任务后,该资源才会被释放。
  • 循环等待:有一组等待进程 {P0, P1,..., Pn}P0 等待的资源被 P1 占有,P1 等待的资源被 P2 占有,……,Pn-1 等待的资源被 Pn 占有,Pn 等待的资源被 P0 占有。

(2)Q:如何解决死锁问题?

  •  死锁的预防

破坏占有并等待:静态分配策略可以破坏死锁产生的第二个条件(占有并等待)。所谓静态分配策略,就是指一个进程必须在执行前就申请到它所需要的全部资源,并且知道它所要的资源都得到满足之后才开始执行。进程要么占有所有的资源然后开始执行,要么不占有资源,不会出现占有一些资源等待一些资源的情况。但这种方式严重降低了资源利用率

破坏循环等待:层次分配策略破坏了产生死锁的第四个条件(循环等待)。在层次分配策略下,所有的资源被分成了多个层次,一个进程得到某一次的一个资源后,它只能再申请较高一层的资源;当一个进程要释放某层的一个资源时,必须先释放所占用的较高层的资源,按这种策略,是不可能出现循环等待链的,因为那样的话,就出现了已经申请了较高层的资源,反而去申请了较低层的资源,不符合层次分配策略。

  • 死锁的避免

银行家算法改善了 资源使用率低的问题 ,但是它要不断地检测每个进程对各类资源的占用和申请情况,以及做 安全性检查 ,需要花费较多的时间。

  • 死锁的检测

当死锁发生的时候,去进行检测,确定发生死锁的进程和资源,然后解决问题。

  1. 如果进程-资源分配图中无环路,则此时系统没有发生死锁
  2. 如果进程-资源分配图中有环路,且每个资源类仅有一个资源,则系统中已经发生了死锁。
  3. 如果进程-资源分配图中有环路,且涉及到的资源类有多个资源,此时系统未必会发生死锁。如果能在进程-资源分配图中找出一个 既不阻塞又非独立的进程 ,该进程能够在有限的时间内归还占有的资源,也就是把边给消除掉了,重复此过程,直到能在有限的时间内 消除所有的边 ,则不会发生死锁,否则会发生死锁。(消除边的过程类似于 拓扑排序)
  • 死锁的解除

当死锁检测程序检测到存在死锁发生时,应设法让其解除,让系统从死锁状态中恢复过来,常用的解除死锁的方法有以下四种:

  1. 立即结束所有进程的执行,重新启动操作系统:这种方法简单,但以前所在的工作全部作废,损失很大。
  2. 撤销涉及死锁的所有进程,解除死锁后继续运行:这种方法能彻底打破死锁的循环等待条件,但将付出很大代价,例如有些进程可能已经计算了很长时间,由于被撤销而使产生的部分结果也被消除了,再重新执行时还要再次进行计算。
  3. 逐个撤销涉及死锁的进程,回收其资源直至死锁解除。
  4. 抢占资源:从涉及死锁的一个或几个进程中抢占资源,把夺得的资源再分配给涉及死锁的进程直至死锁解除。

4、ThreadLocal

(1)对threadlocal的理解

ThreadLocal是多线程中对于解决线程安全的一个操作类,它会为每个线程都分配一个独立的线程副本从而解决了变量并发访问冲突的问题。ThreadLocal实现了线程内资源共享。每个线程内都有一个ThreadLocalMap类型的成员变量。

举个例子:在用JDBC访问数据库的时候,每一个线程和数据库的连接都会放到一个threadlocal里面,这样就能避免线程A中断线程B的连接。

(2)Threadlocal的内存泄露

ThreadLocalMap中的key是弱引用,值为强引用,key会被回收释放内存,而value不会。因此会造成内存泄漏。解决方案:主动remove释放key、value


都看到这里了,给个小心心♥呗~

相关文章:

  • skytower靶机详细教学
  • 第9章:LangChain结构化输出-示例5(基于大模型如何精确匹配POJO的字段)
  • Spring Security功能概述和相关介绍
  • IP------PPP协议
  • 利用 Vue 3 + Vite + Element UI Plus 结合 Service Worker 实现版本管理
  • pandas读取数据
  • 高并发微服务日志管理:ELK、Loki、Fluentd 终极对决与实战指南
  • 矩阵的 正定(Positive Definite)与负定(Negative Definite):从Fisher信息矩阵看“曲率”的秘密
  • Java所有运算符理解
  • SOME/IP-SD -- 协议英文原文讲解5
  • 线代[8]|北大丘维声教授《怎样学习线性代数?》(红色字体为博主本人注释)
  • Chrome 浏览器(版本号49之后)‌解决跨域问题
  • Hi3516CV610开发板ISP调试之——图像ISP在线调试 环境搭建教程
  • 《白帽子讲Web安全》学习:深入解析Cookie与会话安全
  • 银河麒麟高级服务器操作系统在线调整/pro/{PID}/limits文件中nofile的软限制和硬限制参数值操作方法
  • 设计模式-结构性模式
  • QARepVGG--含demo实现
  • 手写系列——MoE网络
  • 【算法系列】归并排序详解
  • Spring Boot集成RocketMQ:真实项目应用场景
  • 菲律宾中期选举结果揭晓,马科斯与杜特尔特家族重回“权力的游戏”
  • 用贝多芬八首钢琴三重奏纪念风雨并肩20年
  • 上海一保租房社区亮相,首批546套房源可拎包入住
  • 雷军内部演讲回应质疑:在不服输、打不倒方面,没人比我们更有耐心
  • 中日东三省问题的源起——《1905年东三省事宜谈判笔记》解题
  • 马上评|这种“维权”已经不算薅羊毛,涉嫌犯罪了