- 2023-08-19·自媒体查看全文#面试# 快手面经 操作系统: 1.线程和进程有什么关系,有什么区别?...Roy:详见博客https://tangjiayang.github.io/2023/08/18/2023%E7%A7%8B%E6%8B%9B%E6%80%BB%E7%BB%93/3520分享
#学习总结# 近期学习了一些知识,小小总结一下 mysql高可用: https://tangjiayang.github.io/2023/09/09/MySQL高可用/ redis底层数据结构学习: https://tangjiayang.github.io/2023/08/15/Redis原理学习/
终于收到一个大厂的oc了🉐
经过前几次的一面失败,昨天终于第一次通过了大厂的一面~ 分享面经#面试# 详情汇总在我的博客https://tangjiayang.github.io/2023/08/18/2023秋招总结-八股/,欢迎大家交流学习! 自我介绍 目前有实习的经验吗? 在哪实习? 将来想在哪工作? 实习的时候主要做什么项目? 主要用到哪些技术栈? 技术方面: HashMap了解吗,简单说一下底层原理,包括如何底层怎么解决hash碰撞的以及底层的数据结构 easy,虽然该说的都答了,但是感觉有点生疏了,回头再看看 追问:那HashMap是线程安全吗? 不是 追问:你会怎么去实现一个线程安全的HashMap? 我们可以参考HashTable、ConcurrentHashMap的底层来做 追问:讲一下HashTable、ConcurrentHashMap的区别 HashTable通过使用Synchronize来加锁实现线程安全,但是它锁住了整个表,会导致效率低下,因此现在不会去使用,用的都是ConcurrentHashMap 追问:那 ConcurrentHashMap 是怎么加锁的? 对底层的HashMap数据结构进行分块加锁,提高了并发度 ⚠️这里,还好面试官没有继续追问,我这边确实就知道这么多了,也是蛮幸运的,之后立刻补一下ConcurrentHashMap!! 你刚提到了Synchronize,java API层面有个 Reentrantlock ,这两个有什么区别? balabala(用CAS的方式修改对象的Mark Word)还好上次面试问到了,准备很充分,答得不错,继续巩固。 你刚才说了CAS,简单说一下他是怎么实现的? 首先CAS指的是 compare and swap,balabala……,预期值,相同执行,不同,重做,但是有个缺点就是会产生ABA问题。 追问:ABA问题怎么解决? 加一个序号、时间戳、、 java自定义的线程池用过吗? 有用过,⚠️这里我其实没用过,是个云玩家,还好没问我在那个场景用的,好好准备一下具体场景 追问:介绍一下线程池比较常用的参数 核心线程数、最大线程数、阻塞队列这些 追问:拒绝策略有哪些? 直接丢弃、丢弃并且报错、阻塞、调用者执行⚠️这里我没记好,有专门的名词的,没记住,就说了含义,马马虎虎,好好记一下 追问:线程池的扩展是怎么个扩展方式? 这个真的老生常谈了 核心线程->阻塞队列->最大线程->拒绝策略 线程的sleep( )方法和Object的wait( )方法有什么区别? ❌!!本次面试第一次翻车,挺基础的问题,没答出来挺可惜的,掉价!! 我说:sleep后还会占据CPU、wait不会占据 实际上:都会让出CPU,sleep不会让出它锁定的资源 对线程之间常见的通信的工具类有了解吗? socket吗? 不是,比如说信号量这些 消息队列,共享内存这些吗? 还不是不是 笑场了一下,面试官给我解释了一下是生产者消费者模型等等 你常用的数据库是什么?(mysql)mysql常见的隔离级别有哪些? 读未提交 读已提交 可重复读 串型化 追问:默认隔离级别是什么?(可重复读)怎么实现的? MVCC机制、快照 (⚠️没说出多版本控制协议) 追问:说一下多版本控制协议 这个还蛮了解的就详细说了一下(最小事务id、当前事务id、当前活跃事务列表、下一个要常见的事务id,理清逻辑还是蛮简单的) mysql中主键、唯一索引、联合索引它们的区别是什么? 主键:只能定义一个主键 唯一索引:特点就是唯一 联合索引:就是联合。。。 ⚠️:感觉这个问题太简单,虽然我各个索引的底层原理都知道,但是就是不知道要说什么,不知道从哪下手。。 SQL语句常见的优化方案有哪些? ⚠️:这个不会,答得不好,就说了优化索引、select的 * 变为具体的每个列 分布式、微服务部分: 你们现在做的项目数据量有多大? ⚠️我其实不知道,瞎说的 几十qps,mysql中一天几千条,主要是工厂中设备的一些参数 数据量其实不大是吧 考虑过分库分表吗? 这里乌龙了一下,我以为是问分布式数据库,哈哈,被面试官纠正了回来。 数据量过大的时候,对数据表进行分库、分表 先分库、再分表⚠️ 对,这是一种模式 然后我就说自己不了解了 所以说就是知道有这个东西,但是没用过对吗? 不不不,用过,我的项目中用到过自己实现的分表 --按照数据量来分表的 分表情况下,你是怎么查询的? ⚠️这是一个蛮好的问题,我说我在创建时就将所属的分表号嵌入其中,从而找到对应的表 我看你还用到了Spring Cloud的一些技术栈是吧,都用到了哪些东西呀? springcloudgateway、nacos(注册中心) 追问:那对于安全登录认证这一块熟悉吗?就是你现在有很多微服务,通过网关把请求转到微服务模块中,怎么识别身份? 我在项目中有做过类似的功能,就是给用户添加AK、SK字段,(⚠️我好像答偏题了他问的是,怎么判断请求是不是恶意伪造的,而我。。暂时没想明白,aksk也还没搞明白,待我好好研究一下) 你是怎么传递的? 明文。。 你的网关还做了什么别的服务吗?比如说降级、日志这些方面的功能吗? 说了流量染色, 追问:这个是通过IP白名单吗? 这个是通过在header头部添加一个字段来保证的,(⚠️这么说ip白名单太合理了,不如顺着说下去,还能跟上个安全认证的问题呼应。。 追问:那它这个会不会被别人劫持? 网络安全嘛,肯定会被劫持,,,,,总没有完美的办法,,不太清楚 对于微服务中常见的RPC框架都知道哪些? Dubbo,其他有点忘记了 有个项目使用dubbo进行了请求转发 就只是用过是不是? 是 场景题: 假如说要优化web服务的吞吐量,这个服务有个接口,这个接口需要查询数据库或者说要对数据库进行一些crud操作,但是因为这个接口比较耗时,导致性能不好,怎么优化? (⚠️很少被问到场景题,有点考验临场发挥) 1. 接口比较慢,可能是接口中做的事情太多了,那么我们可以把里面的业务分阶段 分成多个小阶段的话,它的事务型怎么保证? 我觉得这个情况很适合我做的一个任务框架,,,框架能保证任务失败重试 任务是做了幂等吗? 是 (到底该怎么回答,不知道,快去学习一下) 追问:还有别的方式吗? 2. 可能是sql语句可以优化一下😂 我看你用了redis,用在什么场景? 分布式锁 追问:它是怎么实现的? setnx 这个命令其实是有一些问题的,可能会导致死锁 (⚠️,这个听说过,但是没深入了解,再好好看看) 对微服务常用的比如说限流有了解过吗? redis中放个计数器算是个方案 要不就用springcloud中的配置配一下就好 springcloud中的熔断策略了解吗?比如一个微服务出现了异常,怎么处理? 一种方式是:打到出错主机的请求直接全部报错 (⚠️,理应有很多中策略,但我不够了解) 消息中间件比如kafka了解吗,有用过吗? 准备用😂 主管提问: 你后续想在目前实习的公司工作还是想出来? 必然出来 评价一下你现在待的公司 如果让你接受一个任务,比一个人负责,你打算怎么着手开展工作? 分享一个你在学习、实习期间遇到有很大挑战性的事情,你是怎么解决的,事后你的反思是什么? 如果你的主管给你安排了一个你不感兴趣的任务,这时你应该怎么办? 再参与一个项目任务的时候,如果你的主管/方案设计师的方案与你的想法不一样,怎么办? 追问:如果实际上就是你的想法是正确的,怎么办?
#面试# 美团java后端面经来喽,以下是八股部分 详情汇总在我的博客https://tangjiayang.github.io/2023/08/18/2023秋招总结/,欢迎大家交流学习! 你在里面用了Redis的那些数据结构? 首先我用到了分布式锁,所以用到了String,还有的话就是redis用作缓存的时候,大多数用的都是String 分布式锁用的是什么命令呢? setnx 追问:为什么setnx能起到分布式锁的作用呢? 首先是一个单线程的缓存,线程先query一下看看锁里的内容是不是null,是null就可以占据,。。不太会说,补一下(⚠️) 分布式锁的目的是保证在分布式环境下对资源的互斥访问,即同一时间只能有一个进程/线程能够获取到该资源的锁。SETNX的特性正好满足了分布式锁的要求,其原理如下: 1.当进程 A 需要获取锁时,它尝试使用SETNX操作写入一个指定的锁键(例如lock:resource)。如果该键不存在,则设置成功,进程 A 获得了锁,并可以执行相应的操作。 2.当其他进程 B、C 等尝试获取同一个锁时,如果键lock:resource已经存在,则SETNX操作返回0,表示设置失败,进程 B、C 认为锁已经被其他进程占用,不能获取到锁。 3.进程 A 在完成操作后,释放锁时,通过DEL指令删除锁键lock:resource,从而释放该锁。 追问:为什么setnx之前还要query?setnx不行吗? 我说的是setnx里面会自动先query一下。 追问:分布式锁是怎么保证原子性的呢? (⚠️)定时、所有者id、释放锁时使用lua脚本先判断是不是自己的,再进行释放 由于Redis的单线程执行机制,setnx指令不会受到并发的影响,每个指令都会依次执行。这样,即使多个客户端同时执行setnx指令,也能够确保只有一个客户端成功设置了分布式锁(返回1),其他客户端会失败(返回0)。 你有研究过String底层是怎么实现的呢? 不太了解(❌,redis底层得系统地学一下,老是不会太伤了) 留个坑,系统学完redis底层后来填 如果用redis实现一个计数器,怎么做呢? 就是有一个自增的命令 追问:用哪个数据结构呢? String就可以 追问:命令是什么? 忘记了 incr 追问:你就知道有这个命令是吗,那这个命令能保证原子性吗? 我记得有两个自增的命令,一个可以一个不可以。。 其实:incr和incrby指令都是原子操作 mysql这一块,我有一条sql语句执行很慢,我怎么排查原因呢? 首先就用explain命令看一下索引的执行情况吧,主要看一下type列,如果是all的话,说明使用了全表扫描,我们肯定是要对其建立索引的。 追问:使用explain命令后,重点关注哪些信息? 主要观察索引使用情况,如果使用了索引,再看看使用的索引是否符合我们的预期,如果不是,则进一步分析原因。 (❌) 还是这道题,上次面试刚问了,还没记熟 什么叫回表? 正常情况下(即根据普通索引进行查询时,先查询该记录对应的主键,再去主键所在的B 树中寻找其数据信息) 索引覆盖是什么? 查询的列都在使用的索引或主键中,所以不需要再回主键的B 树中获取记录。 一条SQL的执行过程是什么? MySQL5.8之前有缓冲区,缓冲区会根据sql语句进行缓存、5.8之后移除了;之后会到解析器中进行词法、语法分析,这一阶段还会把 * 换成表中对应实际的列;之后会进入优化器,这里会产生sql语句的执行方案,最后进入存储引擎进行查询。 (⚠️补一下,见https://tangjiayang.github.io/2023/07/27/MySQL原理学习/) 查询缓存的功能是什么?有什么优缺点? 太简单了 维护缓存需要开销,而且缓存命中率很低(很少会在短时间内出现重复的sql,特殊场景除外) mvcc 的原理是什么? mvcc其实是可重复读的实现方式,它是对数据库进行快照的一个操作。具体的实现方式为……(就那四个字段,然后说清楚逻辑即可,详细请看https://tangjiayang.github.io/2023/07/27/MySQL原理学习/ https 的原理是什么? 就知道这是http的一种安全的形式(❌,不会) 追问:安全是怎么实现的呢? 应该是对传输进行了加密 追问:怎么加密的呢? 不是很清楚 HTTPS是一种通过加密传输数据的网络协议,它的原理如下: 1.加密通信:HTTPS 使用了加密算法来保护数据的机密性。在客户端与服务器之间建立连接时,首先会进行一次称为SSL/TLS握手的过程,通过该握手过程来协商加密算法和密钥等信息。然后,通过使用协商的密钥对传输的数据进行加密,确保数据在传输过程中不容易被窃听或篡改。 2.数字证书验证:HTTPS 使用数字证书来验证服务器的身份。数字证书由第三方机构(证书颁发机构,Certificate Authority,CA)签发,包含了服务器的公钥和相关信息。在握手过程中,客户端会检查服务器返回的证书,并验证其有效性和合法性。如果验证通过,客户端会使用证书中的公钥加密传输的数据。 3.对称加密与非对称加密结合:HTTPS 使用对称加密和非对称加密结合的方式来进行加密传输。非对称加密算法用于在握手过程中协商对称加密算法和密钥,这样可以确保密钥在传输过程中不被泄露。对称加密算法用于加密实际传输的数据,对称加密算法的加解密速度更快,保证了性能。 4.安全标识:HTTPS 使用 URL 地址中的 "https://" 来表示当前使用的是安全的加密连接。 总结来说,HTTPS 通过加密通信、数字证书验证、对称加密与非对称加密结合等措施,实现了对传输数据的机密性和完整性的保护。这使得客户端与服务器之间的通信更加安全,可以有效地防止数据被窃听和篡改。 你知道使用http协议通信的客户端和服务器之间是怎么建立连接的、报文是怎么发送的? http是基于TCP协议进行连接,消息传输的……三次握手四次挥手 (❌,偏题了啊。。。)……三次握手很熟练,but四次挥手没看,一下就露出了马脚,被追着问四次挥手 追问:三次挥手不行吗? 当客户端与服务器之间使用HTTP协议进行通信时,建立连接和报文的发送可以分为以下步骤: 1. 建立连接: - 客户端发送一个连接请求给服务器,这通常是通过向服务器的IP地址和端口发送连接请求的方式实现的。 - 服务器接收到这个连接请求后,会进行一系列的握手过程来建立连接。这个过程通常是使用TCP协议来完成的。 - 一旦握手成功,客户端和服务器之间就建立了一个持久的连接,可以进行通信。 2. 报文的发送: - 客户端构建HTTP请求报文,包括请求行、请求头和请求体。 - 客户端将构建好的请求报文通过已建立的连接发送给服务器。 - 服务器接收到请求报文后,会解析其中的内容,包括请求方法、URL、请求头和请求体等。 - 服务器根据请求的内容进行相应的处理,可能是读取数据库、执行业务逻辑等。 - 服务器构建HTTP响应报文,包括响应行、响应头和响应体。 - 服务器将构建好的响应报文通过已建立的连接发送给客户端。 - 客户端接收到响应报文后,会解析其中的内容,包括响应状态码、响应头和响应体等。 - 客户端根据响应的内容进行相应的处理,可能是展示页面、执行下一步操作等。 需要注意的是,HTTP是无状态的协议,每个请求和响应都是独立的,服务器不会记住之前的请求,因此需要使用一些机制(如Cookie、Session等)来维护状态信息。 以上就是使用HTTP协议通信时客户端和服务器之间建立连接和报文的发送过程。具体的实现和细节可能会有所不同,但总体流程相似。 四次挥手把同意对方请求跟自身请求分离开。是因为在客户端请求断开时(客户端发送端->服务器接收端),服务器可能还有数据未发完,所以需要分开操作: ✦先同意对方关闭连接,对方无法传输数据;(第二次挥手) ✦自己若还有数据未发送完,接着发送直至全部发送完毕; ✦请求自身关闭连接;(第三次挥手) 也就是说,三次握手、四次挥手差别就在第二次,有没有把同意对方请求跟自身请求拆分开。 为什么客户端要等2个时长后关闭呢?就是2ML问题? 不清楚(❌) 避免网络不稳定带来的问题, 假如在最后发送ACK请求后直接关闭, 此时由于网络原因Server端没收到, Server就会重试FIN请求 但是此时连接已经关闭了,Server端将无法正常发送报文 或者 发送到错误的地方; 了解http的滑动窗口是什么吗? (❌很模糊,答了个大意---一次传输多个数据块---记错了) 滑动窗口本质上是描述接受方的TCP数据报缓冲区大小的数据,发送方根据这个数据来计算自己最多能发送多长的数据。如果发送方收到接受方的窗口大小为0的TCP数据报,那么发送方将停止发送数据,等到接受方发送窗口大小不为0的数据报的到来。 操作系统线程与进程的区别是什么? 老生常谈,每次都问 java你比较了解是吧,synchronize和Reentlock的底层实现上有啥区别? synchronize用了对象的内部锁,作为对象信息头部中的一个字段,没加锁为空,加锁被设为线程id Reentlock维护了一个变量,加锁 1 ,解锁-1,为0说明没加锁。⚠️ Synchronized 和 ReentrantLock 是 Java 中实现线程同步的两种机制,它们在底层实现上有以下区别: 1. 可重入性(Reentrancy): - Synchronized 是 Java 内置的关键字,具有可重入性(也称为递归性)。这意味着如果一个线程已经获得了某个对象的锁,在释放锁之前,可以多次进入被锁保护的代码块。 - ReentrantLock 是 `Lock` 接口的实现类,也具有可重入性。要使用 ReentrantLock 的可重入特性,需要显式地调用 `lock()` 和 `unlock()` 方法。 2. 获取锁的方式: - Synchronized 是隐式锁,当线程进入同步代码块时,会自动获取锁,并在代码块结束后自动释放锁。无法手动控制锁的获取和释放。 - ReentrantLock 是显式锁,需要手动调用 `lock()` 方法来获取锁,在合适的地方调用 `unlock()` 方法来释放锁。 3. 可中断性: - Synchronized 在获取锁的过程中,如果线程被阻塞,无法被中断,只能等待获取锁的机会。 - ReentrantLock 提供了可中断的获取锁的方式。通过调用 `lockInterruptibly()` 方法,如果线程被阻塞,可以通过调用 `interrupt()` 方法中断线程。 4. 公平性: - Synchronized 是非公平锁,线程在竞争锁时,无法保证获取锁的顺序。 - ReentrantLock 默认是非公平锁,但可以通过构造函数参数设置为公平锁,在锁的竞争中,会按照请求锁的顺序分配锁。 5. 性能: - 在低级别的锁竞争情况下,Synchronized 的性能优于 ReentrantLock。这是因为 Synchronized 是 JVM 内置的锁机制,可以进行一些优化操作。 - 在高度竞争的情况下,ReentrantLock 的性能可能优于 Synchronized,因为 ReentrantLock 提供了更多的灵活性,例如可中断锁、非块结构的锁等。 综上,Synchronized 和 ReentrantLock 在可重入性、获取锁方式、可中断性、公平性和性能等方面有所区别。在选择使用哪种机制时,需要根据具体的情况综合考虑。一般而言,对于简单的同步需求,使用 Synchronized 更为方便;而对于更复杂的同步需求,可以考虑使用 ReentrantLock 提供的额外功能。 Reentlock的底层实现和AQS有啥关系? 答了乐观锁,我的无知再次暴露无遗(❌,我不知道AQS是啥啊。。) AQS(AbstractQueuedSynchronizer),抽象队列同步器 核心思想: 如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。 JVM内存模型是什么样的? 线程私有:PC 、虚拟方法栈、本地方法栈 公有:堆 永久代(java8前)、元数据区(java8后) 元数据区存的是什么? 元空间中有常量池、类的配置信息(⚠️说得好像有问题,复盘) 元空间的实现称为常量池 常量池中存储了以下几种元数据: 1.类信息(Class Information):包括类的名称、父类、接口、访问修饰符等。 2.字段信息(Field Information):包括字段的名称、类型、访问修饰符等。 3.方法信息(Method Information):包括方法的名称、参数类型、返回类型、访问修饰符等。 4.字符串常量(String Constants):包括字符串字面值,例如 "Hello, World!"。 5.字面常量(Literal Constants):包括整数、浮点数、布尔值等字面常量。 类加载机制是怎么样的? (不太熟练) 首先读取类的信息 再进行安全性检查 开辟虚拟内存空间,进行静态变量的初始化 将虚拟地址变为物理地址 再执行构造函数 加载 验证 准备 解析 初始化 使用 双亲委派是什么? 从最早的祖先开始判断能否加载需要的类 子类加载器如果没有加载过该目标类,就先委托父类加载器加载该目标类,只有在父类加载器找不到字节码文件的情况下才从自己的类路径中查找并装载目标类。 保证Java程序的稳定运行,避免类的重复加载:JVM区分不同类的方式不仅仅根据类名,相同的类文件被不同的类加载器加载产生的是两个不同的类 追问:父类怎么知道他能不能加载需要的类? (❌,不了解,一个比较好的问题) 其实很简单: 始祖类加载器判断是否加载过该Class,如果已加载,则返回Class对象 如果没有则尝试从其对应的类路径下寻找class字节码文件并载入(有空去看看源码) 如果载入成功,则返回Class对象;如果载入失败,则委托给始祖类加载器的子类加载器 算法:二叉树,找两个随机节点的最近公共父节点 用递归做更好
#面试# 大厂面经,被面试官拷打八股,还是因为自己的八股准备不够充分,整理了一整天,八股真的很重要!!
#面试# 快手面经 操作系统: 1.线程和进程有什么关系,有什么区别? 定义:进程是一个正在执行的程序实例,具有独立的内存空间和资源。线程是进程的一部分,用于执行进程内的具体任务。 资源占用:每个进程都有自己独立的内存空间和资源,包括文件、网络线程、内核数据结构等。线程共享进程的资源,包括内存空间、文件描述符等。 调度和执行:进程在操作系统中被调度为可执行状态,并并行或交替执行。线程在进程内被调度执行,共享进程的执行环境。 交互和通信:进程之间通常通过进程间通信(IPC)进行交互和通信,如管道、消息队列、共享内存等。线程之间由于共享相同的内存空间,可以直接访问和修改共享变量,实现线程间的通信和同步。 异常和崩溃:进程之间具有强隔离性,一个进程的崩溃不会影响其他进程。线程共享同一个进程的资源,一个线程的崩溃可能会影响整个进程的稳定性。 创建和销毁:创建和销毁进程需要操作系统的调用,涉及加载和卸载程序、分配和释放内存等操作。线程的创建和销毁更轻量级,通常由程序代码直接调用线程库函数进行操作。 2.什么是虚页? 在现代计算机系统中,使用了虚拟内存技术来更好地管理和利用系统的内存资源。虚拟内存通过将物理内存和磁盘空间结合起来,为每个进程提供一个逻辑上连续的地址空间,称为虚拟地址空间。 虚拟页和物理页(也称为页框)之间存在映射关系。操作系统利用页表(Page Table)来维护虚拟页与物理页之间的映射关系。页表的每个表项记录了一个虚拟页与一个物理页之间的映射关系。 当程序访问虚拟地址空间中的某个虚拟页时,操作系统首先在页表中查找对应的物理页。如果映射存在,操作系统将虚拟页映射到物理页并将该页加载到主存中,然后将虚拟地址转换为物理地址,程序就可以在主存中访问该虚拟页所对应的物理页了。 Java基础: 1.Map的实现类有哪些,介绍一下它们: HashMap:HashMap是最常用的Map实现类之一。它基于哈希表实现,可以提供快速的插入、删除和查找操作。HashMap允许存储键值对,其中键是唯一的,值可以重复。它不保证元素的顺序,也不是线程安全的。 TreeMap:TreeMap是基于红黑树实现的有序Map。它会根据键的自然顺序或者自定义比较器对键进行排序。TreeMap的插入、删除和查找操作的时间复杂度为O(logN)。由于它对键进行排序,因此遍历时会按照键的顺序输出。TreeMap不是线程安全的。 LinkedHashMap:LinkedHashMap是HashMap的子类,它保持了插入顺序或者访问顺序(通过构造函数指定)。LinkedHashMap使用哈希表和双向链表实现,因此可以在常数时间内维护键的顺序。LinkedHashMap也不是线程安全的。 ConcurrentHashMap:ConcurrentHashMap是线程安全的HashMap实现。它通过将整个Map分成多个段(Segment)来提高并发访问的性能。这样,在大多数情况下,不同的线程可以同时访问Map,从而提高性能。ConcurrentHashMap不保证元素的顺序。 Hashtable:Hashtable是早期的哈希表实现,它是线程安全的。虽然Hashtable的使用已经不那么常见,但由于它是线程安全的,因此在多线程环境中仍然可以使用。 2.HashMap的应用场景是什么? HashMap是一种常用的Map实现类,适用于以下场景: 快速查找:由于HashMap基于哈希表实现,可以快速进行查找操作。如果需要根据键快速查找对应的值,HashMap是一个很好的选择。 键值唯一:HashMap的键是唯一的,不允许重复。这对于需要存储唯一键值对的场景非常适用,例如存储用户ID和对应的用户信息。 无需排序:HashMap不保证元素的顺序,插入和删除操作的顺序也不会影响其内部结构。如果不需要对键进行排序,只需要快速存储和查找键值对,那么HashMap是一个很好的选择。 非线程安全环境:HashMap不是线程安全的,适用于在单线程环境下使用。如果需要在多线程环境中使用Map,可以考虑使用ConcurrentHashMap。 Java并发编程: 1.为什么要用线程池? - 降低线程创建和销毁的开销:线程的创建和销毁是相对昂贵的操作,涉及到分配内存、初始化上下文等耗时操作。如果每次需要执行任务时都创建一个新的线程,会造成大量的系统资源消耗和额外的开销。线程池通过预先创建一组可复用的线程,避免了频繁的线程创建和销毁操作,降低了系统的开销。 - 控制并发度和资源消耗,避免系统崩溃:线程池可以控制并发执行的任务数量,通过限制线程池的大小和配置线程的数量,可以合理地控制并发度,避免过多的任务同时执行导致系统资源耗尽,系统崩溃。同时,线程池可以对任务进行排队和调度,合理利用系统资源,避免资源的浪费。 - 提供线程管理和监控功能:线程池可以对线程进行统一的管理和调度,包括线程的创建、销毁、复用、暂停、恢复等操作。线程池还可以监控线程的状态、执行情况和资源使用情况等,提供了对线程池的可控和可视化管理。 2.怎么创建线程池? 创建一个ThreadPoolExecutor对象,可以使用ThreadPoolExecutor的构造方法或者Executors类提供的工厂方法来创建。 具体: 创建一个ThreadPoolExecutor对象,可以使用ThreadPoolExecutor的构造方法或者Executors类提供的工厂方法来创建。 设置线程池的核心线程数(corePoolSize),最大线程数(maximumPoolSize),线程空闲回收时间(keepAliveTime)等参数。这些参数决定了线程池的大小和行为。 可选地设置任务队列,可以选择使用ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue等不同类型的队列,根据实际情况来选择最合适的。 提交任务给线程池执行,可以使用execute()方法提交一个Runnable任务,或者使用submit()方法提交一个Callable任务,可以获取任务的执行结果。 关闭线程池,当不再需要使用线程池时,应该调用线程池的shutdown()方法来关闭线程池。 3.线程池有什么运行机制,有什么参数? 启核心线程--->入队列--->启非核心线程--->异常 连接池线程处理不过来,有哪些处理方式? 抛出异常:当任务被提交到线程池中,但无法处理时,线程池会抛出异常。例如,当线程池的队列已满且无法接受更多任务时,线程池可以抛出 RejectedExecutionException 异常。在捕获异常时,可以选择记录日志、发送警报或执行其他适当的操作。 丢弃任务:当线程池无法处理任务时,可以选择丢弃该任务。这样可以减轻线程池的负担,但需要注意确保任务的丢失不会对系统造成重大影响。可以使用 DiscardPolicy 或者 DiscardOldestPolicy 策略来实现任务丢弃。 阻塞任务:当线程池无法处理任务时,可以选择将任务阻塞等待。这样可以保持任务的有序执行,直到有可用的线程来处理任务。可以使用 ArrayBlockingQueue 作为任务队列,通过设置队列的容量来控制线程池的负载。 调用者运行:当线程池无法处理任务时,可以选择由调用者自行处理任务。也就是说,任务由调用线程直接执行,而不是交给线程池处理。这样可以避免线程池负担过大,但也需要注意调用线程是否能够处理任务。 JVM: 1.java常见垃圾回收算法 标记-清除算法(Mark-and-Sweep):这是最基本的垃圾回收算法。它的过程分为两个阶段:标记阶段和清除阶段。首先从根节点开始,标记所有能够被访问到的对象,然后清除未标记的对象。标记-清除算法存在碎片问题,即内存空间会变得不连续,影响后续对象的分配。 复制算法(Copying):复制算法将内存一分为二,每次只使用其中一半。当一半空间被用满后,将存活的对象复制到另一半空闲空间中,然后清除已使用的空间。复制算法解决了碎片问题,但是需要一倍的内存空间,且对象的复制会影响性能。 标记-压缩算法(Mark-and-Compact):标记-压缩算法结合了标记-清除算法和复制算法的优点。它先标记存活对象,然后将存活对象紧凑地放置在内存的一端,然后对整个堆进行压缩,使得存活对象占用连续的内存空间。标记-压缩算法可以解决碎片问题,并且不需要额外的内存空间,但是需要移动存活对象,也会有一定的性能开销。 分代算法(Generational):分代算法基于对象的存活时间进行划分,一般将堆分为新生代和老年代。新生代通常使用复制算法,因为大多数对象在短时间内就会变成垃圾。老年代则使用标记-清除或标记-压缩算法,因为老年代中的对象通常存活时间较长。 2.你了解哪些垃圾收集器? ----这里限于篇幅,只是简要说明,强烈建议读者移步此处更详细地了解各个收集器原理(见章节:1.7.GC 垃圾收集器) Serial收集器:Serial收集器是最基本的垃圾回收器,它使用单线程进行垃圾回收。在进行垃圾回收时,会暂停所有的用户线程。Serial收集器适用于小型应用和客户端环境,它的特点是简单、高效,适用于单核处理器。 Parallel收集器:Parallel收集器使用多线程进行垃圾回收。Parallel收集器通过并行处理来提高垃圾回收的吞吐量,适用于多核处理器和数据量较大的应用。 CMS收集器:CMS(Concurrent Mark Sweep)收集器是一种并发垃圾回收器。它使用多线程和并发处理来最小化垃圾回收对应用程序暂停时间的影响。CMS收集器适用于对响应时间有较高要求的应用,但由于并发执行的特性,可能会增加垃圾回收器本身的开销。 G1收集器:G1(Garbage-First)收集器是一种面向服务器的垃圾回收器。它具有高吞吐量和可预测停顿时间的特点。G1收集器将内存分为多个小区域,并基于优先级进行垃圾回收。它可以通过调整停顿时间目标来平衡吞吐量和停顿时间。G1收集器适用于大内存容量和对停顿时间有较高要求的应用。 设计模式: 1.说三个设计模式,分别是什么意思? 你平时分别在什么情况下用到这三种设计模式? 单例模式 单例模式(Singleton Pattern)是一种创建型设计模式,用于保证一个类只有一个实例,并提供全局访问点。它的核心思想是通过限制类的实例化次数,保证在程序运行期间只存在一个实例。 实现单例模式的常用方法是使用一个私有的静态变量来存储实例对象,同时提供一个公共的静态方法来获取该实例。常见的实现方式有饿汉式(在类加载时即创建实例)和懒汉式(在使用时才创建实例)。 日志管理器:在一个应用程序中,通常只需要一个日志管理器来记录日志,单例模式可以确保只有一个实例用于记录日志。 数据库连接池:在多线程环境下,需要保证数据库连接池的全局唯一性,单例模式可以确保只有一个连接池实例。 工厂模式 工厂模式(Factory Pattern)是一种创建型设计模式,用于创建对象的统一接口。工厂模式通过将对象的实例化过程封装到一个工厂类中来解耦客户端与创建对象的过程。 数据库驱动管理器:在一个数据库操作的应用程序中,可以使用工厂模式来创建各种数据库驱动的实例。 代理模式 目标对象、代理对象和客户端。代理对象实现与目标对象相同的接口,并持有目标对象的引用。客户端通过代理对象来访问目标对象。 代理模式常见的应用场景是在访问对象的前后做一些额外处理,例如权限校验、延迟加载、远程访问等。使用代理模式可以增强目标对象的功能,同时对客户端屏蔽了目标对象的具体实现,增加了代码的灵活性和可维护性。 Spring: 1.spring中的拦截器和过滤器有什么区别? 触发时机: 过滤器:在Servlet容器中,过滤器是在请求进入Servlet容器之前和响应离开Servlet容器之前进行处理的,即在请求和响应的前后都触发执行。 拦截器:在Spring MVC框架中,拦截器是在请求进入Controller之前和离开Controller之后进行处理的,即只在请求处理的前后触发执行。 使用范围: 过滤器:过滤器可以用于所有的Web应用程序,无论是基于Spring的还是其他任何框架。它可以在应用程序的整个生命周期中使用。 拦截器:拦截器是Spring MVC框架特有的功能,只能用于使用了Spring MVC框架的应用程序中。 配置方式: 过滤器:过滤器的配置是在Web应用程序的web.xml文件中进行的,通过配置URL匹配模式来指定要拦截的请求。 拦截器:拦截器的配置是通过实现HandlerInterceptor接口,并在Spring配置文件中进行声明的,可以根据具体的请求路径等条件进行配置。 功能: 过滤器:过滤器可以对请求的参数进行处理、请求的编码进行转换、对响应结果进行过滤等。 拦截器:拦截器可以在请求进入Controller之前和离开Controller之后进行处理,可以进行权限验证、日志记录、性能监控等。 手撕算法部分: LeetCode347. 前 K 个高频元素
分享一下24届校招大厂面试(Java后端)遇到的题目,希望能和大家多多交流,共同进步! https://tangjiayang.github.io/2023/08/18/2023秋招总结/ 博客中只记录了面试遇到的问题以及面试后复盘总结的答案,如果有错误的地方,欢迎大家批评指正(文章下方有评论区)。目前只汇总了一篇面经,之后会持续更新!
最近几天创建了自己的博客 百废待兴 欢迎大家来逛逛[憨笑] https://tangjiayang.github.io/
最近沉迷刷leetcode 很有打怪升级的感觉 劲头十足 希望10号的华为笔试能顺利过150分[奋斗]
爆刷爽法


