秋招后端开发面试题 - Java多线程(上)


目录

  • Java多线程
    • 前言
    • 面试题
      • 线程和进程?
      • 说说线程有几种创建方式?
      • 为什么调用 start() 方法时会执行 run() 方法,那怎么不直接调用 run() 方法?
      • 线程有哪些常用的调度方法?
      • 线程有几种状态?
      • 守护线程了解吗?
      • 线程间有哪些通信方式?
      • 并发和并行?
      • 同步和异步?
      • 为什么使用多线程?
      • 使用多线程可能带来的问题?
      • 什么是线程上下文切换?
      • 什么是线程死锁?
      • 死锁产生的条件?
      • 如何避免死锁?
      • 那死锁问题怎么排查呢?
      • 活锁和饥饿锁了解吗?
      • 线程间如何同步?
      • sleep() 和 wait()?
      • 为什么 wait() 方法不定义在 Thread 中?
      • 说说你对原子性、可见性、有序性的理解?
      • 如何保证变量的可见性?
      • 那说说什么是指令重排?
      • 指令重排有限制吗?happens-before 了解吗?
      • as-if-serial 又是什么?单线程的程序一定是顺序的吗?
      • volatile 关键字?
      • volatile 实现原理了解吗?
      • 解释双重校验锁实现单例的原理?
      • volatile 可以保证原子性吗?


Java多线程

前言

已经找到工作了,分享秋招时的笔记。祝大家都能顺利找到自己心仪的工作。


面试题

线程和进程?

进程(Process):

  • 进程是操作系统分配资源的基本单位,是程序的一个执行实例
  • 每个进程都有自己独立的内存空间,包括代码、数据、堆栈等
  • 进程之间相互独立,一个进程的崩溃不会影响其他进程
  • 进程间通信需要使用操作系统提供的机制,如管道、消息队列、共享内存等

线程(Thread):

  • 线程是进程的一个执行流,是进程的一部分
  • 线程共享进程的内存空间,因此线程间通信相对更容易
  • 线程可以更轻量级地创建和销毁,开销小

联系与区别:

  • 线程是进程的一部分,一个进程可以包含多个线程
  • 进程是独立的执行环境,拥有独立的内存空间,而线程共享进程的内存空间
  • 进程之间的切换开销较大,涉及上下文切换。线程切换开销相对较小
  • 进程间通信通常需要操作系统提供的机制,而线程间通信相对更容易,可以通过共享内存等方式实现

说说线程有几种创建方式?

  • 继承 Thread 类:重写 run() 方法,调用 start() 方法启动线程
  • 实现 Runnable 接口:重写 run() 方法,调用 start() 方法启动线程
  • 实现 Callable 接口:重写 call() 方法
  • 使用匿名类或 Lambda 表达式
  • 使用线程池

为什么调用 start() 方法时会执行 run() 方法,那怎么不直接调用 run() 方法?

  • 调用 start() 方法会创建一个新线程,然后新线程执行 run() 方法,从而实现多线程并发运行
  • 如果直接调用 run() 方法,就会在当前线程执行,不会启动新线程,就不会实现多线程并发执行

线程有哪些常用的调度方法?

  • wait():使线程进入等待状态
  • join():让线程等待另一个线程执行完成
  • notify()唤醒等待中的一个线程
  • notifyAll()唤醒所有等待中的线程
  • yield():让当前线程让出 CPU 资源
  • interrupt()中断线程
  • isInterrupted()检查线程的中断状态
  • sleep():使线程进入休眠状态

线程有几种状态?

![[image-20230329090159344.png]]

  • 初始状态
  • 运行状态
  • 阻塞状态:需要等待锁释放
  • 等待状态:线程需要等待其他线程做出一些特定动作 (通知或中断)
  • 超时等待状态:可以在指定时间后自行返回
  • 终止状态:线程已经运行完毕

守护线程了解吗?

Java 线程分为两类,分为守护线程用户线程

  • 守护线程服务性质的线程,在所有用户线程结束时会自动结束。通常用来执行后台任务,比如垃圾回收、资源管理等。不影响 JVM 的退出
  • 用户线程:用户手动创建的线程,在程序执行期间一直存在,直到主线程执行完成或者被手动终止。用户线程会影响 JVM 的退出

线程间有哪些通信方式?

  • 共享变量:线程通过读写共享内存中的变量进行通信
  • 锁机制:使用锁来限制对共享资源的访问,从而实现线程的同步和互斥
  • wait-notify 机制:一个线程可以通过 wait 方法进入等待状态,等待其他线程调用 notify/notifyAll 来唤醒
  • 使用管道流:管道流可以在两个线程之间进行数据传输
  • 阻塞队列:一个线程可以通过向队列中添加元素来与其他线程进行通信

并发和并行?

  • 并发:系统能够同时执行多个任务,不一定是同时执行,而是通过任务的切换和调度来实现多个任务交替执行
  • 并行:系统中同时执行多个任务,每个任务在独立的处理器上执行,各个任务之间相互独立

同步和异步?

  • 同步:调用一个函数或方法后,必须等待完成并返回结果,才能继续执行
  • 异步:调用一个函数或方法后,不用等待完成,会通过回调函数、事件触发等机制来处理最终的结果

为什么使用多线程?

  • 提高程序性能:在多核 CPU 上,多线程可以充分利用多核并行处理任务,加快程序的执行速度
  • 提高资源利用率:多线程可以充分利用 CPU 和内存等资源
  • 异步处理:多线程可以实现异步操作,例如在后台加载数据、文件下载等,提高系统的响应性

使用多线程可能带来的问题?

  • 内存泄露
  • 死锁
  • 线程不安全
  • 性能问题

什么是线程上下文切换?

  • 线程上下文切换是指在多线程环境中,由于多个线程共享 CPU 的执行时间
  • 当一个线程的执行时间片用完或者需要切换到另一个线程时,操作系统会暂停当前线程的执行,保存其上下文,然后加载另一个线程的上下文,执行线程

什么是线程死锁?

  • 死锁是多个线程因相互等待对方持有的资源而无法继续执行的僵局状态

死锁产生的条件?

  • 互斥条件:资源只能被一个线程占用
  • 请求并保持条件:已经获得资源的进程请求其他资源,同时不释放已有资源
  • 不剥夺条件:已经占有的资源不能其他的线程抢占
  • 循环等待条件:多个进程之间形成环路,每个进程都在等待下一个进程所占有的资源

如何避免死锁?

  • 破坏死锁的四个必要条件
    • 互斥条件:使用资源分级,避免资源互斥
    • 请求与保持条件:线程在获取资源前释放已持有的资源
    • 不剥夺条件:允许抢占已持有的资源
    • 循环等待条件:强制线程按照一定的顺序获取资源
  • 避免无限期等待
    • 设置资源的超时时间,如果超时未获取,线程放弃或者重试
  • 使用死锁检测和解除机制
    • 定期检测系统中是否存在死锁
  • 使用锁的顺序
    • 确定锁的获取顺序,所有线程按照相同的顺序获取锁

那死锁问题怎么排查呢?

  • 确认是否发生了死锁:查看日志,如果出现线程堵塞的异常情况可能是死锁问题
  • 定位死锁位置:通过分析线程堆栈信息,定位出现死锁的代码位置
  • 确认锁的粒度:确认锁的粒度是否过大,如果锁的粒度过大,容易出现死锁问题
  • 检查锁的维护方式:检查锁是否正常释放
  • 分析并发访问情况:了解多线程请求锁的顺序和时间
  • 制定解决方案:根据以上情况制定解决方案,如调整锁的粒度、优化代码逻辑、增加重试机制
  • 测试验证:在实际系统中测试解决方案是否有效,如果没有解决问题,按照以上步骤继续排查

活锁和饥饿锁了解吗?

  • 活锁:两个或多个线程都在互相等待对方执行某个操作,形成死循环
  • 饥饿锁:某个线程一直无法获取需要的资源

线程间如何同步?

线程间的同步需要通过互斥锁、条件变量和信号量等机制来实现

  • 互斥锁
    • 互斥锁是一种最基本的同步机制,可以实现对共享资源的互斥访问。
    • 当某个线程进入临界区进行访问时,其他线程需要等待,直到该线程离开临界区,释放锁资源,才能再继续访问共享资源
  • 条件变量
    • 条件变量是一种用于线程间等待和通知的机制
    • 当某个线程进入临界区后,如果发现共享资源不满足当前需求,那它就可以将自己休眠,释放锁资源,等待其他线程改变共享资源状态并发送通知以后,再重新唤醒
  • 信号量
    • 信号量是一种更高级的线程同步机制,可以实现多个线程之间的同步操作。
    • 当某个线程需要访问共享资源时,请求获取信号量。如果有其他线程正在使用该共享资源,则该线程进入休眠状态等待信号量被释放。当共享资源被释放时,信号量发出通知,并将等待资源的线程唤醒

sleep() 和 wait()?

在多线程中,sleep() 和 wait() 方法都可以暂停线程的执行

  • sleep() 方法不会释放锁,wait() 会释放锁
  • sleep() 方法通常用于暂停执行,wait() 通常用于线程间通信
  • sleep() 方法在暂停指定时间后,自动恢复运行;wait() 方法调用后,需要其他线程调用 notify/notifyAll 方法唤醒
  • sleep() 方法是 Thread 类方法,wait() 是 Object 类方法

为什么 wait() 方法不定义在 Thread 中?

  • wait() 方法用于线程间通信,通过释放对象的锁并让线程进入等待状态,每个对象都拥有对象锁,因此定义在 Object 类
  • sleep() 方法是让当前线程暂停执行,不涉及对象类,因此定义在 Thread 类中

说说你对原子性、可见性、有序性的理解?

  • 原子性:
    • 原子性指一个操作不可分割的,要么完全执行,要么完全不执行
    • 原子性确保了操作的不可分割性
  • 可见性:
    • 可见性指一个线程对共享数据进行修改后其他线程立即能知道这个修改
    • 可见性确保了线程间共享数据的同步性
  • 有序性:
    • 有序性指程序执行的顺序和代码顺序一致

如何保证变量的可见性?

  • 使用 volatile 关键字:将变量声明为 volatile,每次访问该变量时都会从主存中读取最新的值,而不是使用线程的缓存
  • 使用 synchronized 或 Lock:使用同步机制来确保对变量的访问时互斥的
  • 使用原子类:原子类的操作是原子的,可以保证对变量的修改可见
  • 使用并发集合:使用线程安全的并发集合类,如 ConcurrentHashMap
  • 使用线程间通信:可以使用线程间通信机制,确保线程间的操作顺序和可见性

那说说什么是指令重排?

  • 指令重排是处理器和编译器的一种优化手段
  • 通过优化指令的执行顺序,充分利用处理器的执行单元,减少指令的等待时间,提高程序的执行效率

指令重排有限制吗?happens-before 了解吗?

  • 指令重排有限制,有两个规则 happens-beforeas-if-serial
  • happens-before 规则用来定义多线程间操作的执行顺序
    如果操作 A happens-before 操作 B,那么在程序执行中,A 的效果对 B 可见

as-if-serial 又是什么?单线程的程序一定是顺序的吗?

  • as-if-serial 规则是 Java 内存模型的另一个重要原则
    允许虚拟机在保证多线程正确性的前提下进行指令重排,只要重排后的执行结果和原始执行顺序一致即可
  • 单线程的程序一定是顺序执行的

volatile 关键字?

volatile 关键字可以保证可见性有序性

  • 保证可见性:线程读取变量的值都从主存中读取
  • 保证有序性:volatile 关键字可以禁止指令重排

volatile 实现原理了解吗?

  • volatile 实现可见性和有序性的关键在于内存屏障
  • 内存屏障是一种硬件或软件层面的机制,可以确保在特定位置的操作不会发生在内存屏障之前或之后
  • volatile 变量的读写操作会插入内存屏障,保证读写 volatile 变量时不会指令重排

解释双重校验锁实现单例的原理?

双重校验锁是一种常用于实现线程安全懒汉式单例模式的优化方式

  1. 首次检查:在双重校验锁中,首先检查对象实例是否被创建,如果没有才进行同步
  2. 同步块:在单例对象还没有创建的时候,使用synchronized对代码块加锁,并通过两次检查对象是否被实例化,保证只有一个线程执行了同步块内的代码
  3. volatile 关键字:使用 volatile 关键字确保线程之间的可见性和禁止指令重排序,避免出现另一个线程获得不完全初始化的实例
//双重校验锁实现对象单例(线程安全)
public class Singleton {
    private volatile static Singleton uniqueInstance;
    private Singleton() {
    }
    public  static Singleton getUniqueInstance() {
       //先判断对象是否已经实例过,没有实例化过才进入加锁代码
        if (uniqueInstance == null) {
            //类对象加锁
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }} }
        return uniqueInstance;}}

volatile 可以保证原子性吗?

  • volatile 关键字不能保证对变量的操作是原子性

秋招后端开发面试题系列目录
一、Java
1.1 Java基础上
1.2 Java基础下
1.3 Java集合
1.4 JavaIO
1.5 Java多线程上
1.6Java多线程下
二、JVM
2.1 JVM底层原理
2.2 垃圾回收器
2.3 垃圾回收算法
2.4 类加载机制
2.5 运行时数据区
三、MySQL
3.1 MySQL基础
3.2 事务
3.3 索引
3.4 锁机制
3.5 MVCC
四、Redis
4.1 Redis基础
4.2 缓存原理
五、中间件
5.1 RabbitMQ
六、Spring开源框架
6.1 Spring
6.2 Spring MVC
6.3 Spring Boot
6.4 MyBatis
七、操作系统
八、计算机网络
九、设计模式
十、微服务架构
十一、Spring Cloud分布式
11.1 分布式基础
11.2 Spring Cloud
11.3 GateWay
11.4 Nacos
11.5 OpenFeign
11.6 Ribbon
十二、算法
十三、项目

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

android 分区存储(沙盒存储)适配总结

目录 一、分区存储概念 1.外部存储分类 2.分区存储如何影响文件访问 二、分区适配方案 1. 应用分区存储的文件访问规定 (1).应用专属目录--私有目录 (2).共享目录文件--公有目录 2.MediaStore API介绍 3.Storage Access Framework介绍 三、所有文件访问权限 四、总结…

急急急!微信朋友圈删除了怎么恢复?

微信朋友圈是我们与朋友分享生活点滴的重要平台,但有时候微信出现异常,导致我们编辑好的朋友圈被删除了,这时候该怎么办呢? 幸运的是,微信提供了一种简单的方式来恢复已删除的朋友圈内容。微信朋友圈删除了怎么恢复&a…

react 学习笔记二:ref、状态、继承

基础知识 1、ref 创建变量时,需要运用到username React.createRef(),并将其绑定到对应的节点。在使用时需要获取当前的节点; 注意:vue直接使用里面的值,不需要再用this。 2、状态 组件描述某种显示情况的数据&#…

tkinter中是否有必要使用类

1. 问题背景 在使用tkinter编写事件驱动程序时,Fredrik Lundh的教程中提到,创建一个类(App)作为框架,并以类的实例运行程序,这样会更好,而不是直接启动程序。 以下是问题: 在tkin…

【Go语言快速上手(五)】文件操作协程操作

💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:Go语言专栏⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你学习更多Go语言知识   🔝🔝 GO快速上手 1. 前言2. GO语言的文件操…

武汉星起航:挂牌上海股权中心,开启资本新篇章助力跨境电商飞跃

2023年10月30日,武汉星起航电子商务有限公司在上海股权托管交易中心成功挂牌展示,标志着这家在跨境电商领域拥有卓越声誉的企业正式迈入了资本市场的大门。对于武汉星起航来说,这不仅是其发展历程中的一个重要里程碑,更是对其业务…

Python学习笔记------异常

当检测到错误时,Python解释器就无法继续执行了,反而出现一些错误提示,就是所谓异常(bug) 1、异常的捕获方法 任何程序运行过程中都可能出现异常,我们可以在力所能及的范围内,对可能出现的bug提…

C语言趣味代码(四)

这一篇主要编写几个打字练习的小程序,然后通过这些小程序的实现来回顾复习我们之前学过的知识,然后通过这写打字练习的小程序来提升我们的打字技术和编程技术。 1. 打字练习 1.1 基本打字练习 1.1.1 基本实现 首先我们来制作一个用于计算并显示输入一…

ip网络广播前置放大器SV-7031 接纯后级功放

ip网络广播前置放大器SV-7031 接纯后级功放 感谢您使用我们的网络前置放大器SV-7031。 SV-7031具有1路AC200V OUT电源输出,1路AC200V IN电源输入;RJ45 网络接口,具有网络音频广播、本地广播功能。 18123651365 ip网络广播前置放大器SV-703…

vue-cli+vue3+vite+ts 搭建uniapp项目全过程(一)

unapp官方提供了cli 脚手架创建 uni-app 项目的文档 Vue3/Vite版要求 node 版本 18、20使用Vue3/Vite版创建不会提示选择模板,目前只支持创建默认模板 本文以vue3vitets为例 1、初始化项目 npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project 执行完生成…

就业班 第三阶段(tomcat) 2401--4.28 day1 tomcat1安装配置及单机多实例

企业 Tomcat 运维 文章目录 企业 Tomcat 运维一、Tomcat 简介1、Tomcat好帮手---JDK2、安装Tomcat & JDK1、系统环境说明2 、安装JDK3、安装Tomcat 二、Tomcat目录介绍1、tomcat主目录介绍2、webapps目录介绍3、Tomcat配置介绍(conf)4、Tomcat的管理…

Linux-管道通信

1. 管道概念 管道,是进程间通信的一种方式,在Linux命令中“ | ”就是一种管道,它可以,连接前一条命令,和后一条命令,把前面命令处理完的内容交给后面,例如 cat filename | grep hello …

本地部署 Meta Llama3-8b

Meta Llama 3(8B) Instruct model performance Meta Llama 3(8B) Pre-trained model performance 使用 Ollama 运行 Llama3 访问 Tags llama3,选择你想运行的模型,例如,8b-instruct-q8_0 拷贝并运行命令,ollama run llama3:8b-…

秋招后端开发面试题 - Java语言基础(上)

目录 Java基础上前言面试题Java 语言的特点JVM JDK JRE什么是跨平台性?原理是什么?什么是字节码?采用字节码的好处是什么?Java 和 C 的区别?注释?关键字关键字 instanceof类型转换关键字 this 和 super关键字 final finally fin…

Android数据恢复:如何在手机上恢复丢失的文件和照片

我们都有 我们错误地从手机中删除重要内容的时刻。确实如此 不一定是我们的错。其他人可以对您的手机数据执行此操作 有意或无意。这在某个时间点发生在我们所有人身上。 但是,今天市场上有各种各样的软件可以 帮助恢复已删除的文件。这些类型的软件被归类为数据恢复…

Linux命令大全 以及搭建hadoop

Liunx系统目录 ├── bin -> usr/bin # 用于存放二进制命令 ├── boot # 内核及引导系统程序所在的目录 ├── dev # 所有设备文件的目录(如磁盘、光驱等) ├── etc # 配置文件默认路径、服务启动命令存放目录 ├── home # 用户家目录&#…

UEFI安全启动模式下安装Ubuntu的NVIDIA显卡驱动

UEFI安全启动模式下安装ubuntu的nvidia显卡驱动 实践设备:华硕FX-PRO(NVIDIA GeForce GTX 960M) 一、NVIDIA官网下载驱动 1.1在浏览器地址栏输入https://www.nvidia.cn/drivers/lookup/进入网站,接着手动驱动搜索,并…

【C语言】:动态内存管理

1、为什么要有动态内存分配2、动态内存管理函数2.1 malloc2.2 free2.3 calloc2.4 realloc 3、常见的动态内存的错误3.1 对NULL指针的解引用操作3.2 对动态开辟空间的越界访问3.3 对非动态开辟内存使用free释放3.4 使用free释放一块动态开辟内存的一部分3.5 对同一块动态内存多次…

stm32单片机开发三、DMA

DMA其实就是一种将ADC的数据寄存器、串口的数据寄存器等等一些数据放到sram中特定位置,方便CPU去读取 比如ADC转换,DMA直接转换的ADC的值放在内存中的特定位置,CPU可以直接去读取 uint16_t AD_Value[4]; //定义用于存放AD转换结果的全局…

告别繁琐!小浪助手助你轻松下载学浪视频

小浪助手,一站式学浪视频下载工具,能让你轻松下载学浪视频 工具我已经打包好了,有需要的自己取一下 学浪下载器链接:https://pan.baidu.com/s/1djUmmnsfLEt_oD2V7loO-g?pwd1234 提取码:1234 --来自百度网盘超级会…
最新文章