ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java 5年经验面试题集:从JVM到分布式,深度与广度全解析

Java 5年经验面试题集:从JVM到分布式,深度与广度全解析 我最近帮几个团队做模拟面试发现一个挺有意思的现象简历上写着“5年Java开发经验”的候选人一聊到锁升级、AQS、分布式事务这种题很多人就明显接不住了。倒不是说他们代码写得差而是这些年一直在业务需求里打转底层原理和架构取舍反而成了软肋。这篇《Java开发工程师5年以上经验面试题集》不是让你背题用的而是想帮你画一条线5年这个节点面试官到底在考察什么哪些题必须答出深度哪些地方最容易翻车。我整理了工作中真实高频出现的面试题按模块拆开讲每道题都附上回答思路和容易踩的坑。1. 5年经验的Java面试和初中级到底差在哪里先别急着看题捋清一个前提5年经验的面试考察逻辑和1-3年完全不同。应届生和初级开发者面试官看的是基础扎不扎实、CRUD能不能扛住到了5年很多团队是按“技术骨干”“项目Owner”的预期来招人的也就是说你不但要能写代码还要能扛模块、能排查线上故障、能在技术选型的时候说出为什么。我面过不少人资历写在纸上都很好看但一开口就露馅了。常见的表现有两种一是深度停留在“会用”层面比如知道synchronized可以加锁但问“偏向锁和轻量级锁的区别”就卡住二是广度不够只熟悉自己那一亩三分地问分布式锁、消息队列、缓存一致性完全没概念。5年经验面试的分水岭我认为体现在五个维度维度初级/中级5年以上知识深度知道API和用法理解原理、源码、设计动机故障排查能定位明显Bug能从现象反推链路定位根因性能优化会用工具能从JVM、SQL、架构三层做取舍分布式听说过概念能说清一致性、幂等、CAP取舍技术选型别人用什么用什么能对比方案结合业务给结论所以这篇文章的题目虽然是“面试题集”但本质是帮你补上“深度”和“广度”这两块短板。下面的题都是我实际问过、也被别人问过的高频题目有些看着基础但是往深了挖每一道都能聊二十分钟。2. JVM与内存调优这几个问题答不好后面基本不用面了JVM是5年经验面试的“送分题”也是“送命题”。送分是因为考点高度固定JMM、类加载、GC、OOM翻来覆去就这些送命是因为大家都背过概念但一追问细节、一给场景很多人就变成了背书机器。2.1 线上CPU飙高你怎么排查这是线上故障最典型的场景。候选人有没有实战经验这道题一测就知道。标准链路应该是top -Hp看哪个线程CPU高记下线程号jstack导出线程栈把线程号转成十六进制在栈文件里搜如果卡在GC线程大概率是Full GC频繁再jstat -gcutil看GC频率和耗时如果卡在业务线程看是锁等待还是死循环锁等待会停在park或wait死循环会停在某个方法调用上。很多5年候选人能走到第二步但第三步就断了。他们知道用jstack却不知道配合jstat看GC或者不会用jmap导堆转储。这里有个我踩过的坑jstack刚导出来的时候线程栈是不准的因为采样是瞬时的一个线程可能在几毫秒内从IO切到CPU密集所以最好多导几次间隔几秒对比差异才靠谱。更深一层的问法是GC线程为什么CPU高这背后可能是内存分配过快、晋升阈值设置不合理、或者有大量短期对象。顺着这条线面试官想听的是你能从“看到现象”到“定位根因”的完整链路。2.2 内存区域与OOM的对应关系OOM是个大帽子但5年经验的人不能只说“内存不够了”要能区分是哪块区域出的问题堆内存OOM最常见java.lang.OutOfMemoryError: Java heap space。通常是大集合没释放、或者批量加载了过多数据。反过来问一句堆OOM能不能靠调大Xmx解决不能那只是推迟崩溃时间必须先找到谁在占内存。元空间OOMMetaspace常见于动态生成类CGLIB代理、热部署。很多人在堆上查了半天其实是元空间爆了。栈溢出StackOverflowError不是OOM但更常见递归没出口、或者调用链太深。直接内存OOMDirect buffer memoryNIO用多了没释放。这个最坑默认MaxDirectMemorySize等于Xmx但它不归GC管要盯着。每种OOM的排查手段不一样。堆OOM用jmap -dump导堆再结合MAT分析元空间OOM看加载了哪些类、谁在反射生成类直接内存OOM看Netty或RPC框架的ByteBuffer池。2.3 双亲委派为什么非要这么设计这也是高频题。双亲委派的核心动机是防止Java核心类库被篡改。如果每个ClassLoader都自己加载java.lang.String那类库有多份类型系统就乱了同一个类在不同ClassLoader眼里是不同类型。面试官通常会追加一个问题怎么打破双亲委派有三次机会一是Thread.currentThread().getContextClassLoader()典型的是JDBC驱动SPI机制需要反过来让父加载器加载子类路径的类二是重写findClass这是扩展不算真正打破三是重写loadClass真正打破父优先策略热部署框架Tomcat就是这样的。说个细节很多人不知道JDK9之后双亲委派从“继承优先”变成了“模块优先”ClassLoader的源码注释里明说了要覆写findClass而不是重写loadClass。面试时能补一句这个演进观感会好很多。2.4 G1还是ZGC选型依据是什么5年经验最好别停留在“G1分Region能预测停顿”这种层面。要能说出实际场景下的取舍G1JDK9之后默认目标是“在可控停顿下兼顾吞吐”适合堆大小在几十GB以内、响应时间要求几毫秒到几十毫秒的业务。Real-world里90%的Java服务用G1就够了。ZGC目标是把停顿压到10ms以下甚至几毫秒通过染色指针和读屏障做并发标记和移动适合超大堆上百GB、对RT极其敏感的场景比如证券交易柜台。JDK21的ZGC已经支持分代收集吞吐量提升明显但默认还是G1。面试官更想听的其实是你线上用什么为什么比如“我们用的是G1-XX:MaxGCPauseMillis50然后观察实际的P99 GC停顿发现大对象分配频繁所以把-XX:G1HeapRegionSize调到了16MB”。这种话一念出来面试官就知道你调过参、看过监控不是背八股。3. 并发编程深水区不再问synchronized而是问锁升级与AQS底层并发模块是5年面试拉开差距的核心区域。初级问“volatile和synchronized区别”中级问“ConcurrentHashMap结构”5年就要问“AQS的CLH队列到底怎么工作”“锁升级的临界条件是什么”。3.1 synchronized锁升级的全过程synchronized在JDK6之后是“先偏向、再轻量、后重量”的升级路径这个是基础。但5年面试会追问偏向锁撤销条件多个线程竞争、以及调用hashCode时偏向锁会撤销。我实际排查过一次诡异现象代码里明明没有竞争但性能就是上不去最后发现是代码里调了System.identityHashCode直接导致偏向锁批量撤销JDK15之后偏向锁直接废弃就和这个设计有关系。轻量级锁的自旋轻量级锁靠CAS抢抢不到就自旋自旋超过阈值-XX:PreBlockSpin默认10次升级成重量级。但实际上自适应自旋会动态调整次数JDK里这个参数已经被忽略了。重量级锁的等待队列重量级锁背后是ObjectMonitor有Entry Set和Wait Set两条队列。网上很多文章把这两条队列混为一谈其实wait()和notify()走的是Wait Set没抢到锁的线程在Entry Set谁先拿锁由操作系统调度决定没有严格的“先来后到”。3.2 ReentrantLock为什么不用synchronized的monitorReentrantLock的核心是AQS。5年面试必问“讲一讲AQS的实现”这道题能看出你是背过面试题还是真读过源码。AQS的骨架就是一个volatile int state加一个CLH变体队列。以ReentrantLock的lock()为例核心路径是compareAndSetState(0, 1)尝试快速获取锁失败就addWaiter(Node.EXCLUSIVE)把当前线程封成NodeCAS挂到队列尾部acquireQueued里循环如果前驱是head再次tryAcquire否则shouldParkAfterFailedAcquire检查前驱状态并LockSupport.park挂起线程前驱节点释放锁之后unparkSuccessor唤醒后继节点。很多人能背到第3步但问“Node的状态字段waitStatus有几种”就说不全。答案是CANCELLED1、SIGNAL-1、CONDITION-2、PROPAGATE-3初始0。其中的SIGNAL表示“我释放的时候需要唤醒你”理解了这个AQS的链表唤醒逻辑就通了。再追问公平锁和非公平锁的区别非公平锁在lock()时先CAS一把抢不到才进队列公平锁直接进队列hasQueuedPredecessors()检查前面还有没有排队的。实际业务里非公平锁吞吐更高因为减少了线程挂起/唤醒的上下文切换但可能出现“插队”导致等待线程饿肚子。3.3 ThreadLocal的内存泄漏被迫问出来算我输ThreadLocal是并发模块里最容易被问爆的题因为它和内存泄漏绑定在一起很能测候选项到底是“背答案”还是“真踩过坑”。泄漏原因每个Thread内部有ThreadLocalMapkey是弱引用ThreadLocalvalue是强引用。ThreadLocal被GC回收后比如线程池里的线程还活着但ThreadLocal对象已经没引用了key变成nullvalue却清不掉形成了以null为key的Entry。解决方式不是“用完remove就行”这么简单。我见过的务实做法在finally块里remove()这是底线设计上让ThreadLocal存的对象是轻量级的不要存大对象、不要存HttpServletRequest这种有上下文引用的线程池场景下尤其注意线程复用导致“脏数据”比如上个请求的用户信息被下一个请求读到这比泄漏更可怕。面试时能说出“我用ThreadLocal存traceId但发现线程池复用后traceId串了后来每次请求进来先set、finally里remove”这种真实场景比背十遍“弱引用强引用”都有说服力。4. 分布式与数据一致性5年经验的必战场到了5年分布式几乎是简历标配。哪怕你写的是“单体系统”面试官也会问“如果拆成微服务数据一致性怎么保证”。这块答不好前面JVM聊得再好也悬。4.1 分布式锁Redis SETNX够不够用这道题是绝对的“分水岭题”。思路清晰的人会立刻分出三个层次第一层基础用法SET key value NX PX 30000。注意一定要“NXPX”一起用分开两步执行就是坑——先SETNX成功再EXPIRE中间进程挂了锁就永远不释放。第二层释放锁的原子性释放锁得先把value唯一标识拿出来比对是自己才删。这个“比对删除”不是原子的要用Lua脚本。Redisson的unlock()内部就是这么做的一段Lua先GET比对再DEL。第三层主从切换锁丢失在Redis主从架构下客户端A在主节点拿到锁主节点挂了从节点接管后没有这把锁客户端B又能拿到锁了。RedLock就是为解决这个问题提出的但它在工程上争议很大。面试时能说出这个缺陷然后补一句“Redisson的watchdog虽然能续期但主从切换的窗口依然存在所以极重要的场景里我倾向用Zookeeper的临时顺序节点watch机制”印象分会很高。4.2 TCC和本地消息表怎么选数据一致性是5年经验的核心考题。可以这样说没有分布式的项目也要问一遍“如果引入分布式你怎么做”原因很简单面试官想知道你有没有全局观。2PC的协调者单点和阻塞问题太明显现在问得少。TCC是Try、Confirm、Cancel三段式适合对延迟敏感、业务愿意为一致性写补偿逻辑的场景比如库存扣减。但TCC的难点不在框架而在于空回滚、悬挂、幂等这三个工程细节绝大多数候选人都答不上来。本地消息表是“最终一致性”的最稳方案业务操作和写消息放进同一个本地事务然后异步扫表投递消息到MQ。它的优点是实现简单、可靠缺点是消息表本身成为瓶颈、实时性差一点。现在很多团队用同库发MQ比如RockeMQ事务消息替代了手动建消息表。面试官其实不期待你写代码而是想听你做“取舍”什么场景允许弱一致什么时候必须强一致比如订单创建后发短信最终一致完全够但转账余额扣减你肯定不想出现“A扣了B没加”的情况。4.3 幂等设计的三板斧分布式题里“幂等设计”是必问的而且特别务实。记住三个工具唯一键约束数据库唯一索引兜底比如支付订单号状态机订单状态流转只能“已支付→已发货→已签收”重复请求打到已签收就直接拒绝Token预发放前端先拿一个幂等Token后端把Token存Redis处理请求时先DELDEL成功才处理DEL失败说明重复提交。能再多说一句“幂等键的生成规则要考虑业务语义”就更好了。比如“用户ID订单号操作类型”做Hash而不是全局用一个UUID——UUID虽然唯一但没法定位业务遇到问题排查效率极低。4.4 Seata的AT模式用起来真没那么简单不少团队引入Seata但面试是考理解的。AT模式是基于“全局锁undo_log”的改进版2PC业务SQL执行前记录undo_log执行后把数据快照和全局锁信息同步给TC事务结束时删除undo_log回滚时借助undo_log反向补偿。有一个使用细节很多人踩过AT模式对SQL有要求比如UPDATE必须走主键或索引否则会拿锁的范围变大并发性能直线下降。我在项目里见过因为一条联表UPDATE导致整个分布式事务的性能从几百TPS掉到几十TPS的后来改成拆两步操作才把数据恢复过来。面试时能说出这种“性能陷阱”和“解决方案”明显比“我们有用Seata”更有分量。4.5 CAP和BASE少说理论多说在你项目里的取舍“CAP不可能三角”这种题5年经验的人不但要能说理论还要能结合项目谈。我的回答模板供参考我们交易链路要求强一致所以用了分布式事务保证ACID但意味着可用性会降一点我们报表链路能接受十分钟延迟所以用的异步MQ加对账任务做到最终一致就可以。接着他会问“那你们怎么对账”你就可以展开对账任务每天凌晨跑比对本地库和支付回调的状态发现不一致的捞出来重新处理或者发告警人工介入。这就是BASE思想在工程里的落地。5. 框架与中间件源码题看没看过源码一问便知5年经验面试Spring、MySQL、Redis、Kafka这四样基本跑不掉。不是考你背API是考“你知不知道它内部是怎么跑的”。5.1 Spring Bean生命周期说全11步Bean生命周期是Spring模块的“必考送分题”但很多5年的人也说不出全流程。标准答案可以这样组织实例化构造器或工厂方法属性填充populateBean按名字或类型注入Aware接口回调BeanNameAware、BeanFactoryAware、ApplicationContextAwareBeanPostProcessor.beforeInitializationPostConstructInitializingBean.afterPropertiesSet自定义init-methodBeanPostProcessor.afterInitialization如果处于使用中正常被调用PreDestroyDisposableBean.destroy 自定义destroy-method。面试官常追加的问题是“你项目里用过BeanPostProcessor做什么”。我建议准备一个真实案例比如“我们写了一个日志增强的PostProcessor扫描带TraceLog注解的方法自动生成AOP切面避免每个接口手动写日志”这一句话就能证明你能把源码知识用在工程上。5.2 MySQL索引为什么B树为什么最左前缀MySQL索引题5年经验必须答出“为什么”为什么不用B树B树的非叶子节点也存数据意味着同样大小的Page里存得更少树更高、IO更多为什么不用HashHash适合等值查询但不适合范围查询而SQL里BETWEEN、ORDER BY、LIKE abc%都有范围语义为什么最左前缀联合索引是“先按第一列排再按第二列排”所以跳过第一列相当于索引整体失效。比如(a, b, c)索引where b1 and c2用不上但where a1 and c2能用到a那一部分这就是“索引下推”的用武之地把c的过滤下推到存储引擎层。另一个容易翻车的是“回表”。如果查询需要的列在辅助索引里没有就要回聚簇索引再查一次。覆盖索引using index就是为了避免回表。能结合EXPLAIN里的Extra字段说明“这个查询用到了覆盖索引Extra显示Using index”面试效果非常加分。5.3 Redis持久化和缓存雪崩、穿透、击穿Redis这块5年经验至少要被问两轮。一轮是“RDB和AOF怎么选”一轮是“缓存雪崩怎么预防”。RDB是fork子进程做快照文件紧凑适合备份恢复但fork大实例时可能卡顿写时复制导致页表复制开销大。AOF以日志追加实时性高但日志膨胀需要定期BGREWRITEAOF重写。我接触过的成熟项目多数是“AOFRDB混合持久化”也就是AOF重写时生成RDB格式的基快照再叠加增量日志。缓存雪崩的预防除了设置过期时间加随机抖动比如5分钟±30秒这种常规操作还可以走“两级缓存”本地Cache先扛住再让打到Redis的流量降级。缓存穿透的解法无非是空值缓存布隆过滤器但要注意布隆过滤器有误判率不能百分之百相信“布隆说不在就一定不在”恰恰相反布隆说“可能在”才要回源。5.4 Kafka为什么快刷盘、顺序写、零拷贝Kafka的高性能三板斧是5年面试的性格题顺序追加写Kafka的Partition是append-only的日志顺序写磁盘比随机写快几个数量级这是“便宜又大碗”的优化页缓存用操作系统的PageCache读写都走缓存消费端读数据不用经过JVM堆这是“零拷贝”的基础sendfile零拷贝传统IO是“磁盘→内核缓冲→用户缓冲→socket缓冲→网卡”sendfile把用户态这趟省了数据直接从内核缓冲进网卡。再追加一个“为什么消息不支持随机读”的问题没有随机读Kafka才能做顺序IO这是磁盘物理特性决定的。为什么Kafka能当“消息队列”又能当“存储”用因为它把日志当作唯一的truth消费只是游标移动。6. 设计模式与架构设计从“会写”到“会设计”5年经验面试设计模式题目很少直接问“单例有几种写法”而是给场景让你自己搭方案。这里我把最常见的几类真题列一下。6.1 用策略模式干掉if-else这题有标准解场景题一般是这样 你负责一个支付模块支持微信、支付宝、银联、余额四种支付方式怎么设计低级答案if (type wechat) ... else if (type alipay) ...每次新增渠道都改核心类测试全回归。标准答案定义一个PayStrategy接口每种支付方式一个实现类注册到Spring容器里用一个Map维护channelCode → strategy新渠道来了加一个实现类核心逻辑不用动。这就是开闭原则。再扩展一步可以用PostConstruct或者ApplicationContext.getBeansOfType自动注入连Map的维护都省了。有个加分项是“有些支付方式需要异步回调、有些要同步直连”所以策略模式里每种的差异不只是算法还有流程编排这时候可以再叠加模板方法定义流程骨架下沉到子类。6.2 观察者模式在订单状态机里的应用还有一道我特别爱问的场景题订单从“创建→已支付→已发货→已签收→已完成”每一步都要触发一堆动作发短信、更新库存、通知财务、记录日志你怎么设计如果你说“在Service里按顺序调用”那每个状态变更都会依赖一堆具体类。观察者模式的思路是订单状态变更时发布事件一堆Listener监听这个事件各自的动作互相不依赖。对于异步动作短信、通知监听器里可以再丢到MQ实现解耦削峰。框架层面Spring的ApplicationEventPublisher就是干这个的用EventListenerAsync比直接注入N个Service干净太多。另外状态机本身用StateMachine比如Spring Statemachine可以管理“合法流转”防止跳状态。6.3 DDD不是让你背概念的考察点是聚合边界架构设计题5年经验的人最好懂一点DDD。面试官不会让你背“实体、值对象、聚合根”的定义而是给一个业务场景让画限界上下文。比如“电商系统怎么做领域划分”你要能说出订单上下文、库存上下文、支付上下文、营销上下文它们之间通过领域事件通信而不是共享一个大OrderService。聚合根是另一个容易含糊的点。我面试时经常举一个例子Order和OrderItem谁更该是聚合根答案是Order因为OrderItem脱离Order没有存在的意义是订单内的部分订单的操作加商品、改数量、提交都必须通过Order这个门面。很多人只说“两个实体、一对多”那就和“贫血模型CRUD”没区别看不出架构设计能力。6.4 设计一个短URL系统检验你的容量思维这道题我不分年限地用来考“设计能力”。5年经验的答案应该长这样发号器用数据库自增或Snowflake把数值转成62进制短码长度6位62^6 ≈ 568亿足够业务量了写路径发号器生成ID写DB再写Redis缓存读路径先查本地Cache没有查Redis没有再查DB301还是302跳转选302。因为301会被浏览器缓存拿不到点击分析的数据需要处理“短码被猜到的风险”号段加签名校验或者适当混淆。这样的回答一步步展示容量预估、读写分离、缓存分层、以及一个容易被忽略的业务决策301 vs 302面试官会觉得你真的设计过系统而不是背了一套“高并发八股”。6.5 秒杀系统的核心点避免超卖和削峰秒杀同样是5年面试的“常青树”。它的核心不是“高并发”而是“不要超卖”和“把峰值打掉”。库存预扣下单时先扣减Redis库存Lua保证原子性扣成功才写订单失败了直接返回“已抢光”。削峰请求先进MQ消费者按库存量拉单处理控制数据库压力。“异步削峰”比同步直写DB好得多。限流每个用户单设备单接口限流令牌桶或者滑动窗口。分层校验前端按钮置灰网关层拦截未登录业务层再查黑名单多一道少一道流量。兜底数据库最终扣减要对得上账如果Redis和DB数据不一致对账任务发现后告警。这套题的答案没有标准形式但你要让面试官看到你脑子里有“流量从入口到DB的多层防线”。7. 实操类场景题笔试题与线上故障的真实考场有的公司5年面试也会出机考或手写代码题但和校招的算法题完全不同基本都是“工程向”题目。所以我单独开一节讲“实操题”这些题不刷到你写不出来刷到过就能稳拿。7.1 手写深拷贝浅拷贝太容易被一眼看穿搜索引擎热词里有“java对象深度拷贝”这道题几乎年年出现。手写一个deepCopy方法很多人的第一反应是序列化public static T T deepCopy(T obj) throws Exception { ByteArrayOutputStream bos new ByteArrayOutputStream(); ObjectOutputStream oos new ObjectOutputStream(bos); oos.writeObject(obj); oos.flush(); ObjectInputStream ois new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); return (T) ois.readObject(); }这段能跑但有三个坑对象必须实现Serializable否则直接抛异常transient和static字段不会被拷贝很多人忽略性能极差序列化要对整个对象图做反射大对象深拷贝会非常慢。更可靠的方案是手动递归拷贝只处理业务需要深拷贝的字段。比如“订单里有List 我要考的是你能不能正确地把List和其中的对象也new出来”而不是依赖序列化。能提一句“实际项目中我用ORM的实体转VO时更常用BeanUtils浅拷贝手动设置需要深复制的字段因为深拷贝大多数时候是反模式”会显得你有经验。7.2 判断字符串是否只含字母和数字避开正则陷阱搜索热词里有一道“java 判断字符串中是否不是字母和数字”看似基础其实能挖出性能坑。常见答案是Pattern.matches(^[a-zA-Z0-9]$, str)。对于短字符串没问题但如果这个方法要在一个高QPS的接口里被调用正则每次都会编译一次Pattern类内部有缓存但肯定比直接判定慢再遇到极端输入还可能退化成灾难性回溯。稳妥写法是遍历字符public static boolean isAlphanumeric(String str) { if (str null || str.isEmpty()) { return false; } for (int i 0; i str.length(); i) { if (!Character.isLetterOrDigit(str.charAt(i))) { return false; } } return true; }代码量差不多但避免正则编译、避免回溯问题。这个场景就属于“基础但不简单”能审出你有没有性能意识。字符串有中文时Character.isLetterOrDigit对汉字返回true如果业务只要字母数字需要改判定规则ASCII区间判断。7.3 行级权限的实现别只答“加个WHERE”“行级权限java”是搜索热词里的一个典型业务题。常见需求是销售只能看自己的客户销售主管能看本组客户财务能看全部客户。如果每段业务代码都手动加WHERE条件漏一处就是越权事故。成熟做法是在DAO层做统一拦截定义一个数据权限注解标注在Mapper接口方法上用MyBatis拦截器在SQL执行前动态拼接权限过滤条件根据当前登录用户的角色和归属机构生成不同的WHERE条件。这就是规则引擎拦截器思路。但面试官会追问“你自己拼SQL怎么防止SQL注入”。你要回答拦截器里拼的是参数占位符而不是直接拼接字符串权限字段值是从认证上下文里取的不是客户端传的。这两个细节一注意行级权限方案才落地。7.4 定时任务框架Quartz、XXL-Job怎么选“java定时任务框架”是热词里出现过的搜索需求。5年候选人不应该只会说“我用Scheduled”要说得出来它的局限单机、无管理界面、失败没有重试。重任务要上分布式调度框架。Quartz提供了最基础的调度能力持久化用JobStroe能应对简单的单机定时任务。但到了分布式环境“同一时刻只能有一个节点跑任务”是需要你手动用分布式锁去协调的。XXL-Job带控制台、执行器和调度器分离支持失败重试、分片广播、动态调整Cron。我实际项目里用得最多部署也简单。ElasticJob当当开源内部直接依赖Zookeeper做分布式协调适合已经有ZK的团队。选型时还有一个点要提任务分片。比如你有100万个用户要推送通知不能一个任务跑到底可以按节点数分片每个节点处理总数/分片数这样能充分利用多台机器而不是一台打满其他空闲。7.5 Controller防爬虫说说方案分层“java controller层 如何防护 防止爬虫”这条热词很特别但特别贴近实战。防爬虫不是单一技术而是一套“分层拦截”的组合拳网关/过滤器层限流令牌桶、IP黑名单、陌生UA标识识别业务层接口签名AppKey时间戳签名防止直接调接口热数据设置访问频率对明显爬虫的UA或高频IP返回验证码挑战数据层敏感字段加密返回列表接口支持游标分页防止爬虫到全量数据业务兜底加入风控规则比如同一账号一小时内请求超过N次自动触发人机验证。要提醒的是防爬虫的核心是“延迟爬虫的边际收益”不能让正常用户受干扰所以在过滤器里做限流时要放行登录用户、放行静态资源避免误伤。7.6 外部排序和TopN大数据文件的极限内存操作如果你面试的是大数据偏多的业务“java排序”“大数据文件排序”也可能被问到。比如一个文件里有几千万组数字内存只有256MB怎么排序答案是外部排序内存放不下的部分分块排序写临时文件最后归并。每块比如50MB排序写完落盘然后多路归并出完整顺序。TopN问题更有实战价值从几千万条记录里找排名前100的销售额不用全排序维护一个大小为100的小根堆新元素大于堆顶就替换复杂度从O(nlogn)降到O(nlog100)。注意这里细节是优先队列默认是小顶堆取前K大要用小顶堆。8. 我面了上百人之后总结出的几点判断标准这篇文章写了这么多题最后说点面试之外的感悟。我每年面几十个人发现能拿高薪offer的候选人和普通候选人的差距往往不在“会不会背答案”而在“有没有在项目里真实地解决过问题”。首先别把面试当考试把它当成一次技术交流。你答不上的题可以直接说“这个我确实没有深究过但我的理解是……”面试官最怕的是你明明不懂还在那边硬扯反而浪费时间。我反过来会告诉你“这个问题不会考”本身就是一种信号说明你的知识边界被量化了。其次所有原理类的知识一定要挂到具体业务上。synchronized锁升级、AQS队列、B树结构这些你在项目里可能一辈子都不需要改源码但遇到线上性能问题你会不会把思路引到“是不是多线程竞争导致重量级锁膨胀”“是不是索引没覆盖到导致回表”才是这些原理真正的用武之地。最后一点5年经验是一个坎过了这个坎面试官真正想看的是你对一个系统的“整体把握能力”。你能画出自己负责模块的架构图能说清楚每个环节的高峰流量、故障预案、数据一致性保障这比任何一道面试题的标准答案都值钱。如果你正准备跳槽建议把这篇文章里的题按模块过一遍每一道都试着用自己的项目经历去讲讲不顺的标记下来查漏补缺。祝你能在面试里把节奏握在自己手里遇到任何题都能沉着地说出“这个问题我们当时是这样处理的……”。
返回列表