常用八股总结(自用)

📅 发布时间:2026/7/7 18:17:14 👁️ 浏览次数:
常用八股总结(自用)
喜欢可以看看博主的网站哦m1kasaz - m1kasaz的博客接口和抽象类的区别接口是对行为的抽象抽象类是对本质的抽象是对一个类具有多种功能就能实现多个接口但是它所具有的属性在被创建的时候就确定了所以只能继承一个类。在使用习惯上抽象类一般用于代码的复用接口更多是为了规范与解耦。JDK8与JDK17新特性JDK8引入了函数式编程的概念引入lambda表达式增强了streamAPI的可以对stream流进行过滤查找映射等操作引入LocalDate等线程安全的时间API允许接口提供方法实现永久代被元空间取代JDK17密封类允许类指定哪些类可以继承他们提供了新的随机数APIhashmap数据结构是什么?为什么hashmap在jdk1.8引入红黑树?hashmap扩容机制?数组链表红黑树当哈希冲突发生次数过多链表过长导致查询速率降低这时候红黑树的查询复杂度优于链表在首次插入元素的时候初始化为16然后每次插入前都会判断当前容量是否超过阈值若超过则扩容为原来的两倍为什么数组长度为2的幂次方?为什么jdk7到jdk8要把头插改尾插?为什么解决hash冲突的方式为链表红黑树?有几个点计算元素索引时更快包括刚放入hash表中和重新放入hash表中二进制位与运算实现更均匀的哈希分布也是二进制位与运算避免头插法造成循环链表导致死循环在结点数量大于等于8时转化为红黑树在结点数量小于6时退化为链表这样可以对大规模冲突时的时间复杂度进行优化也可以减小小规模冲突时的性能开销ArrayList与LinkedList区别arraylist基于数组可以直接根据下标获取元素查询复杂度为O1插入删除复杂度为On需要扩容扩容有一定的开销linkedlist基于链表查询复杂度为On默认最大长度Integer_Max一般不需要扩容插入删除复杂度为O1cocurrentHashMap如何实现?从jdk7到jdk8做了什么升级?基本实现和hashmap一致cocurrenthashmap固定了扰动因子且引入了CAS技术和锁机制jdk1.7对hash表加入分段锁jdk1.8提高了锁细粒度单独为每个桶加锁且在元素更新时进行CAS判断元素值经过valitle关键字修饰保证了可见性什么是乐观锁?CAS实现原理?什么是悲观锁?synchronized和reentrantLock的区别?乐观锁认为资源被访问时是安全的只有在资源将发生变动的时候才对资源进行操作维护三个数当前值预期值写入值发生修改前只有当预期值和变量值一致才发生修改否则拒绝然后重复或者放弃。悲观锁认为资源的访问是有风险的资源被访问前需要加锁synchronized和reentrantlock都是可重入锁synchronized基于JVM而reentrantlock基于JDKreentrantlock的功能更强大可以实现锁超时公平/非公平锁锁唤醒和放弃等待synchronized原理以及锁升级s锁的状态分为无锁、偏向锁、轻量级锁、重量级锁堆区域的对象内存分为对象头MarkWord、实例数据、对齐填充当我们创建一个锁对象他的MarkWord默认是无锁状态即偏向锁标志位为0后两位为00如果此时有一个线程申请这把锁首先在线程内生成一条锁记录该线程会通过CAS方式根据锁记录将锁对象的MarkWord更改为偏向锁状态偏向锁标志位置1后两位为01并将线程id写入如果有别的地方尝试获取锁首先会判断锁记录的线程id是否一致若一致则认为这是一次重入将锁记录设置为null不一致则认为这是一次竞争该锁升级为轻量级锁轻量级锁则是每次申请锁的时候都会进行CAS操作且CAS操作会将锁记录和markword进行一些交换如果当前锁对象被持有那么申请锁的线程就会不断进行CAS操作尝试获取当出现竞争则升级为重量级锁重量级锁锁对象现在会维护一个Monitor这个Monitor会分为Owner、Entrylist、Waitset三种结构当有线程申请锁会先去Owner看看有没有线程占有如果有他将自旋后进入entrylist阻塞队列其余调用了wait方法的线程会进入waitset等待队列原子类如何实现原子类通过添加volatile关键字和CAS实现最经典的实现就是atomicintegerarray其中的自增方法就是基于该思想volatile关键字的作用volatile主要解决以下问题任何对该变量的写操作都会立即写入主内存中任何对变量的读操作都会从主内存获取最新版本禁止了指令重排序通过插入内存屏障保证编写顺序与运行顺序一致JMM内存模型?指令重排序?happens before原则?java内存模型是自己定义的内存模型保证java程序在不同操作系统下正常运行定义了java并发编程需要遵守的规则分为主内存和本地内存主内存存储存放堆全局变量和被volatile关键字修饰的变量本地内存存放虚拟机栈、本地方法栈指令重排序程序执行的顺序与编写的顺序并不一致可能经过编译器重排、指令并行重拍等可能出现并发问题happens-before原则保证操作前后的可见性定义了一些规则程序顺序规则一个线程内按照代码顺序书写在前面的操作 happens-before 于书写在后面的操作解锁规则解锁 happens-before 于加锁volatile 变量规则对一个 volatile 变量的写操作 happens-before 于后面对这个 volatile 变量的读操作。说白了就是对 volatile 变量的写操作的结果对于发生于其后的任何操作都是可见的。传递规则如果 A happens-before B且 B happens-before C那么 A happens-before C线程启动规则Thread 对象的start()方法 happens-before 于此线程的每一个动作。AQS队列如何实现?如何实现公平锁?AQS队列是一个双向链表线程以结点方式插入链表刚插入时会自旋对前一个结点的锁状态进行判断然后才会阻塞阻塞的结点需要前驱结点的唤醒同时拥有头尾指针用于遍历链表以Reentranlock为例获取锁之前先判断该锁的状态如果该锁空闲则判断队列中是否有等待时间更长的线程如果有则让出该锁。线程池的核心参数?提交任务流程?核心线程池如何计算为什么?核心线程数、最大线程数、阻塞队列长度、线程存活时间、时间单位、线程工厂、拒绝策略首先判断核心线程池是否满若未满则创建新线程执行任务若满则判断阻塞队列是否满若未满则入队若满则判断最大线程池是否满若未满则创建新线程执行任务若满则执行拒绝策略任务分为CPU密集型n1和IO密集型2*n为什么需要全局唯一id大量数据必将导致分库分表这将导致原本自增的id变得毫无意义且id自增规律过于明显容易出现数据安全问题为什么选用雪花算法与uuid对比uuid是128位的id生成器存储消耗更大uuid生成的id是无序的索引建立的难度较大。与redis对比引入了redis的强依赖性且向redis请求需要计算网络开销对比雪花算法直接在本地生成没有优势。为什么要实现异步秒杀去耦合降低模块之间的依赖程度提高性能将判断逻辑和读写逻辑分开且异步执行比串行执行效率高很多为什么选用MongoDB而不是MySQLMongoDB的数据结构更灵活对话场景通常包含多次数据库写操作MongoDB在这方面是优化过的他的并发性能比MySQL更好MongoDB内置了自动分片功能在高数据量场景下分片比MySQL更方便为完成对话记忆功能我们需要存储的是聊天记录文本信息占绝大部分而Bson又非常适合存储文本信息。用户对话可能非常长记录非常多而且我们需要存储多个用户数据单一的MongoDB服务器肯定满足不了需求我们需要建立分片集群MongoDB是支持分片集群的而且可以很方便的添加实例缓存穿透大量无用请求缓存中不存在到达后端布隆过滤器、设置无用字段缓存击穿数据过期时大量请求到达后端由于数据过去请求将访问数据库造成数据库崩溃互斥锁查询缓存失败尝试获取锁获取成功则重写redis返回结果获取失败继续重试逻辑过期不设置过期时间设置一个过期时间字段若逻辑过期则尝试获取锁成功则新开线程修改redis其他获取锁失败的线程返回过期数据数据淘汰策略lru最长时间未使用、lfu最近最少使用、直接报错、删除剩余过期时间最小的redis使用场景分布式锁、消息队列list、stream、点赞系统bitmap分布式锁在主从集群中保证分布式锁的思想红锁保证在一半以上的结点加锁主从数据同步原理高并发全量同步从节点尝试与主节点建立连接发送自身replid、offset主节点判断replid与自身不相等则先返回自身的replid与offset从节点保存版本信息主节点执行bgsave生成RDB文件且记录RDB期间的所有命令先发送RDB文件然后发送repl_baklog日志文件从节点根据这些信息同步主节点数据增量同步从结点重启或数据变化但是主从replid一致主节点会根据从节点的offset发送repl_baklog日志文件从节点根据日志文件同步哨兵sentinel高可用监控主观下线某sentinel发现某redis实例未在规定时间内响应。客观下线超过一半的sentinel认为其主观下线。选主规则判断节点断开时间、优先级越小越优先、优先级一样看offset自动故障恢复通知分片集群多个master每个master可以拥有多个slave且master间相互监控。读写数据引入哈希槽概念对key值取余后决定它由哪个master管理Redis网络模型IO多路复用技术redis的性能受限于网络IO多路复用技术在用户态与内核态交换数据时用户态不会等待内核态的数据就绪而是内核态主动将已经准备好的数据放入用户态然后用户态直接获取。redis的网络模型redis的网络模型是IO多路复用的一种实现多个应用的socket通过IO多路复用技术和事件派发来调用redis服务而redis在有关网络IO的处理都是多线程的这样就提高了降低了网络时延Redis常见数据类型和底层实现Stringint、embedded String当字符串以整数形式存入时redis将会以int类型存储embstr是redis自定义的字符串类型当字符串长度小于等于44字节将以该形式存入内存当字节数大于44将以raw原生字符串存入内存这两者的区别是embstr的数据是连续的而raw的数据不是Listziplist和linkedlist两者结合就是quicklist链表里每一个结点都是一个在内存中连续的链表结合了ziplist和linkedlist两者的有点Hashziplist、hashtable当键值的长度都比较小时将以ziplist作为数据结构每个key后紧跟着一个value形成[key1,value1,key2,value2]的形式当键值长度过大时转换为hash表的形式Setintset、hashtable当存入的数据都是整型时使用intset不满足条件时使用hashtable但存入hashtable的只有键值全为nullZSetziplist、skiplisthashtable当存入元素的数量在一定范围内时使用ziplist以[member1,score1,member2,score2]形式存储当元素数量超出一定范围后使用skiplisthashtable进行存储hashtable维护每一个成员和对应的分数skiplist用于快速定位某个成员bitMap和MySQL实现签到功能对比如果使用MySQL我们需要维护多个字段包括年月日是否补签等字段当用户量很大时需要很大的开销分析需求其实用户签到是一个0 1问题可以用位图很好模拟MySQL三大日志redolog物理日志数据页发生更改时将更改信息写入日志保证数据库崩溃后的恢复保证数据的持久性和完整性数据在被查询时将数据写入buffer pool缓冲池中将操作过程记录在log buffer中然后InnoDB会根据一定的策略将buffer中的数据刷盘到redolog中常见的策略有事务提交buffer空间不足此外InnoDB后台还会有一个线程每一秒将log buffer的内容写入page cache文件缓存中然后调用fsync刷盘。日志并不是单独存在而是多个日志形成一个循环链表边写边刷binlog逻辑日志执行语句时将语句写入日志保证数据的一致性有三种格式statement直接将语句写入row不直接记录记录一个引用通过工具解析出详细信息mixed时混合形式语句会引起不一致则用row否则用statement每个线程都有一块binlog cache用于保存binlog然后系统根据一定策略调用fsync将cache写入磁盘两阶段提交简单来说就是为redolog和binlog日志加一层事务保证两者都能同时提交。具体来说redolog的第一次写入为预提交状态然后会写入binlog最后才会一起提交undolog逻辑日志记录每一个事务对数据的修改当前读和快照读的区别是什么当前读如select update/shared in mode lock 和增删改操作使用的读取为读取字段加锁防止其他事务修改保证读取的是最新版本数据快照读读取时生成一个readview快照通过这个快照到undolog查找当前最新可见版本的数据然后返回这种读取不需要加锁并发性能高MongoDB集群复制集群主节点从节点主负责写从负责读主挂掉后从节点选举出新的主节点主从间通过oplog来同步数据分片集群每个分片有不同的职责有负责conf配置的有负责路由转发的有将数据拆分到不同分片hash分片和范围分片项目桂美社交购云平台这是一个基于springboot的项目业务功能主要参照了小红书用户通过验证码登录登录后自动签到可以发动态查看附近的店铺信息购买店铺的一些团购券也可以查看他人发布的动态也可以对笔记进行点赞大概就是这么多。自己负责的模块项目的亮点和难点全局id生成器其实不是很难异步秒杀消息推送Reentrantlock和Synchronized怎么选大部分情况下选sc锁sc锁是jvm原生关键字经过多次优化他的性能已经非常好可以实现例如多线程自增类。少部分sc锁无法完成的功能可以选择rt锁如要实现某个支持超时等待的任务或者任务基于等待时间优先被执行或者你希望你的任务可以被其他任务打断。rt锁的锁中断机制可以使用的场景防止死锁当线程处理一些费时任务时可以主动打断可重入都是可重入锁但是实现机制不太一样sc锁通过线程持有计数每重入一次就将这个计数加1释放就减1计数为0则将该锁释放rt锁内置了state变量获取锁1释放锁-1为0时锁被释放sc锁是非公平锁final和static关键字有什么用final关键字可以修饰变量、方法、类修饰变量上声明该变量为常量不可以被改变修饰方法表示该方法不能被子类重写修饰类表示该类不能拥有子类既不能被继承final关键字就和他的名字一样表示你这次操作的对象是这个对象的最后一个版本他不能再改变static关键字一般修饰变量和方法表示该方法和变量不属于某个对象而是属于类本身将变量或方法的存储位置固定在方法区JDK 8的元空间与类信息在一起。ThreadLocal的内存泄漏问题一般情况下使用是不会发生内存泄漏的内存泄漏主要发生在我们在一个局部方法里使用了threadlocal且我们向其中加入了大量对象或者一些大对象然后不停的调用该方法然后我们知道springboot内集成的tomcat服务器会复用线程然后threadlocal的生命周期和线程一样导致线程在被销毁的时候其中的弱引用key也被销毁key指向的value会留在内存中导致内存会出现一大块无法回收的区域继而导致OOMerror和exception的区别error是指你的程序出现错误特别指那些无法处理的错误情况比较严重例如栈溢出、内存不足exception是指程序中出现的异常这些异常是程序本身的错误可以被处理没那么严重例如空指针、数组越界继承、组合、多态继承一个类子类继承另一个类父类的方法是子类基于已存在的类进行扩展强调是一个的关系比如fish是一个animal继承可以实现代码的复用快速创造新类组合一个类完整包含了另一个类强调有一个的关系比如car有engine引擎这一个类这种方法可以降低代码的耦合性实现一个类快速继承多个类的功能多态允许父类的引用指向子类对象根据子类的类型决定父类调用的具体是什么方法强调的是怎么做的问题为什么Long类型在方法内加1在方法外却不变主要是因为以下两点java在方法传参的时候是通过值引用的方式进行传递他会创建一个对象副本然后将这个新的副本传递给方法作为形参使用。Long类型是一个不可变的包装类一旦被创建其内部的值就无法修改hashset底层实现hashset底层完全由hashmap实现在插入的时候value会指向一个静态的虚拟对象PRESENTtry-with-resourse的本质和使用try后面的参数是实现了autoclosed类的对象这些对象将在try执行完后自动关闭实现一个无需finally语句就能将资源自动关闭的效果java常见数据结构ListArrayList、vector、LinkedList、CopyOnWriteListMapHashMap、LinkedHashMap、TreeMap、HashTable、ConcurrentHashMapSetHashSet、TreeSet、LinkedHashSet值类型和引用类型内存存储方式不同赋值逻辑的差异参数传递的差异为什么要在项目中引入AI其实一开始我没有做AI优化的打算但实际体验这个平台后我发现他的一些服务是不够完善的比如我们虽然会通知后台订单的状态发生变化但是我们也需要投入人力到后台检测而且单纯依靠人力也有可能造成一些人为的失误所以我想借助AI完成这些重复的工作但其实我深入了解以后AI其实可以帮助我们完成更多的需求于是我就不断在对这个项目进行更新。先修改缓存还是先修改数据库两种方式都有可能发生不一致问题先更新缓存再修改数据库如果在数据库被修改之前因为缓存丢失其他线程的请求到达数据库并且将数据库中的脏数据写入了缓存最后数据库才更新完毕这就出现了数据库和缓存不一致。先更新数据库在更新缓存查询到一个过期的缓存然后去数据库中查询到一个旧数据正要将这个旧数据写入结果另一个线程抢先一步将新的数据写入了然后旧数据又覆盖上去造成不一致。所以我们必须引入延时双删策略具体是我们更新数据库前先将缓存删除然后在更新数据库之后延迟一段时间再次删除缓存这就使得缓存出现旧数据的可能性大大降低BIO和NIOBIO阻塞IO每个线程单独管理一个连接当数据没有准备完成线程就会一直阻塞等待NIO多路复用IO一个线程负责处理多个连接数据没准备好直接去检查下一个channel而不是阻塞他的实现基于三个组件Channel、Buffer、SelectorChannel是一个双向通道可以传输数据数据一般存储在缓冲区Buffer中Selector是实现轮询的关键组件当调用它的select方法时它会去调用内核函数epoll.wait这个调用会阻塞直到有一个就绪的IO事件hr面试的问题和回答对加班的看法个人并不排斥相信公司的决定业务紧急特殊情况做过最有挑战性的事搭建网站坚持最久/最骄傲的事搭建网站、坚持在网站上发布博客挫败的事当时大一去面试一个推文部门自己没什么准备被面试官冷眼相待了当时路线没有规划好去学了一些无用的知识至少对于我现在求职没有帮助感觉自己浪费了许多时间为什么选择我们公司我通过查看贵公司的官网和技术博客了解到贵公司不仅提供基础的云服务还在“智能规则库AI”防火墙等领域提出解决方案我对“项目AI”的解决方案非常感兴趣而且通过与面试官的交流还了解到贵公司正在进行大模型接口的开发所使用的技术栈和我的专业技能匹配程度较高我非常渴望能够参与到这类项目的团队开发中来JVM的垃圾回收我们保存在堆区的对象需要定时回收这样才能保证堆区的可用性。标记垃圾的过程引用计数法根据对象被引用的次数判断无法解决循环引用问题可达性分析法根据GC Root栈、方法区、锁构成的有向图判断对象的存活。具体过程为将要遍历的对象分为三种白色遍历后不可达的对象即垃圾灰色已经遍历了但还没有继续向下遍历黑色已经遍历了确认不是垃圾标记后我们需要清除垃圾标记清除法老年代标记好后原地清除垃圾可能产生内存碎片标记整理法老年代将存活对象移动到一块连续内存直接移除剩下对象不会产生碎片性能低复制法新生代将存活对象复制到另一块内存直接移除剩下对象复制比移动快但是空间利用率低了分代回收新生代对象经理多次Young GC后仍存活就晋升到老年代。具体过程是对象新生在eden区eden区满触发YGCYGC后交换from和to的对象如果对象在经过15次GC后仍存活则会移动至老年代如果老年代和新生代都装不下了就会触发FGC。具体FGC采用什么算法还需要看具体的垃圾回收器。垃圾回收器并行垃圾回收器paralell系列全程STW不能和用户线程同时进行并发垃圾回收器1.进行直接标记与GCroot直接关联的对象短暂STW 2.扫描整个引用链确认垃圾和存活对象 3.重新标记STW 4.垃圾清理CMS使用标记清除算法容易产生内存碎片使用传统的新生代/老年代分区适配小堆G1使用标记复制算法划分多个region回收时优先对垃圾较多的region回收适配大堆MySQL的MVCC机制简单来说MVCC就是通过undolog版本链和readview快照来确定事务之间的数据可见性。具体来说undolog会记录某个数据的版本通过回滚指针将他们连起来根据它可以访问到数据的各个版本。readview有多个属性当前事务id所有活跃且未提交的事务ids活跃且未提交最小事务id下一个事务id数据版本小于当前事务最小事务id的数据属于可见大于下一事务id的版本为不可见在两者之间时就需要判断其是否在未提交的ids中在则不可见不在则可见。MVCC解决的是不可重复读和部分幻读问题。具体来说事务分为当前读和快照读。快照读在第一次访问数据时生成一个readview后续访问该数据沿用之前的readview这就解决了不可重复读当前读增删改、share in mode、for update是每次都会获取数据的最新版本且在对该数据进行操作时加入临键锁避免了部分幻读Redis的RDB和AOFredis用于持久化存储的方式。RDB就是将某一时刻的数据集压缩以后存放在rdb文件中AOF就是以命令的形式保存在aof文件中。具体来说RDB拥有save同步和bgsave异步两种方式来执行这个快照操作。AOF将命令先写入aof_buf中然后调用系统内核的write命令写入操作系统缓存中最后刷盘到aof文件磁盘中。他们两者都会应文件过大而使用各自的压缩方式。具体来说RDB会存储特殊的二进制文件AOF则会在分析多行命令后将多行命令压缩为一行来存储。两者对比RDB的故障恢复会更快RDB只需要对二进制进行译码即可而AOF的命令文件则需要实打实的再执行一次比较耗时AOF的性能消耗更小对数据文件进行快照比记录命令更加消耗性能。Redis和MySQL的数据一致性问题要研究这个问题就要弄明白以下问题为什么选择删除缓存而不是更新缓存如果更新缓存意味着缓存和数据库都要放数据所以更新比删除只有数据库有数据操作发生数据不一致的概率会更高为什么先更新数据库在删除缓存旁路缓存简单来说先操作数据库比先操作缓存更安全。具体来说如果先操作缓存那在我写入数据库之前如果有读操作更新了旧数据到缓存中最后新数据更新到数据库中就很容易出现不一致。但是先更新数据库也会有风险不过比先操作缓存风险低得多。强一致性与低延时的取舍要保证强一致就加读写锁保证只有同一时间只有一个线程操作缓存和数据库。但是这种方法会导致服务器的延迟比较大。要选用低延迟就要设计先更新数据库异步删除缓存的方法但要承担短期内数据不一致的风险。Spring的AOP、beanAOP的设计初衷是实现代码复用、降低代码间的耦合性通常用于实现日志、限流、鉴权、事务、缓存等业务。使用AOP主要编写Aspect切面类和切点Pointcut它的实现原理是动态代理具体来说你使用autowire等注解注入对象的时候Spring返回的是一个经过切面事务处理的代理对象。bean的生命周期包括调用beandefinition方法获取定义信息创建赋值销毁赋值过程又包括依赖注入、实现Aware接口、实现后置处理器和初始化方法。bean的循环依赖问题spring三级缓存singletonObjects已经完成所有创建过程的单例对象、earlySingletonObjects完成部分过程的早期bean对象、singletonFactories存放对象工厂用于创建某个对象MQ 的三大核心作用标准八股名词业界通常将其总结为 6 个字解耦、异步、削峰。解耦 (Decoupling)比如 A 系统调用 B、C、D 系统不用硬编码对接。A 把消息扔给 MQ谁需要谁自己去订阅。异步 (Asynchronous)比如用户注册主流程只要写完数据库就返回成功。发短信、发邮件这些耗时操作扔给 MQ 异步去做大大降低接口响应时间。削峰 (Peak Shaving/Load Leveling)比如秒杀场景瞬间 10 万个请求打过来数据库会直接挂掉。让请求先进入 MQ 排队系统按照数据库能承受的最大阈值比如每秒 1000 个平滑地拉取消费。TreadLocal内存泄漏ThreadLocalMap的 Key 是ThreadLocal实例本身的弱引用而 Value 是我们存进去的对象的强引用。一旦外部没有强引用指向ThreadLocal对象下一次 GC 时 Key 就会被回收变成null。但只要当前线程不死Thread - ThreadLocalMap - Entry - Value这条强引用链就一直存在Value 永远无法被回收。在现代后端架构中我们全部使用的是线程池。核心线程执行完任务后是不会销毁的它会被放回池子里接下一个任务。如果上一个任务没清理干净脏数据不仅会占用内存还可能被下一个用户的请求读到造成极严重的业务安全事故比如串号、越权。项目架构框架jdk17、springboot依赖管理maven存储redis、mysql、mongodb、云存储远程服务opensearch监听etcd