ARTICLE DETAIL

资讯详情

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

小米服务端实习笔试全解析:从Java基础到并发场景设计

小米服务端实习笔试全解析:从Java基础到并发场景设计 如果你准备投小米的服务端开发实习岗网上能找到的那份“小米2018春季实习生服务端开发工程师笔试题”是一张很好的标尺。我当年也啃过类似的大厂笔试后来断断续续参与过几次校招出题和评审现在回头再看这类题最大的感受是它真正考的不是难题偏题而是你有没有把基础东西吃透。很多同学准备笔试就是闷头刷LeetCode但小米这份题提醒我们服务端开发岗的考察逻辑和纯算法岗是有明显差别的算法只是其中一块Java基础、并发、网络、数据库、场景设计都会出现而且经常混在一起考。这份题虽然标注的是2018年但里面涉及的考察结构和知识模块直到现在做服务端开发的招聘依然高度一致。技术面试的核心考察点其实是稳定的变化的是具体题目描述和热度那些“万年常考”的知识点几乎没有变过。这篇文章我会从几个核心模块展开结合题目常见考法和实际做题思路讲清楚每个模块应该怎么准备哪些地方容易翻车以及考完笔试之后下一步该做什么。不管你是正在准备小米实习还是想摸清大厂服务端笔试的套路这篇都能给你一些能直接落地的参考。1. 笔试定位小米服务端实习笔试到底在考什么1.1 和小米业务绑定的岗位画像小米和其他互联网公司不太一样的地方是它的业务线非常杂手机系统、IoT智能硬件、电商、金融、互联网服务、云计算后端技术栈也很多样。但这道实习笔试的题目其实并没有去考小米特定的内部框架而是集中在计算机基础和服务端通用技术上。这说明了一个朴素的事实实习生的笔试公司不指望你已经会他们的业务但是很希望你已经有了扎实的底层功底进组之后能快速上手。从公开可查的题型和参加过笔试的同学反馈来看这份笔试的核心模块大致分为四块算法与数据结构、Java基础与并发、计算机网络与操作系统、数据库与场景设计。有些年份会加一些Linux基础题有些会掺一道手写设计题。整体风格是“选择题 编程题 简答题”混合时间大概两小时题量不算少需要合理分配。1.2 从热搜词反推大家最关心的问题这里有个很有意思的现象搜索这个标题的人往往还会搜“小米OS4内测答题答案”“小米澎湃OS4内测申请答题答案”“小米beta答题答案”等等。这说明很多人在准备小米相关内容时天然有一种“找答案”的心态而不是真正去理解题目背后的知识点。但无论是内测答题还是实习生笔试题直接背答案的效率其实很低题目稍微变个形式你就不会了。关于小米内测答题这些我就不展开说了逻辑是一样的你得理解这个系统为什么这么设计而不是背一个答案。对于服务端开发笔试来说更是如此。真正的做法是建立一套自己的“考点地图”知道每个题目在考察什么然后针对性地补短板。这也是我写这篇文章的出发点不是给你一份“标准答案”而是帮你看清这份题背后希望你掌握的能力模型。1.3 线上笔试的基本规则与时间分配建议大厂的线上笔试一般是通过牛客网或者其他OJ平台进行的小米这些年基本也是这个模式。整场大概120分钟题目包括多选、单选和两到三道编程题。选择题覆盖范围广每道题大概一到两分编程题分值高通常是20到30分一道。这里我建议的时间分配策略是拿到卷子先花2分钟扫一遍所有题目大概判断难度。选择题卡住超过3分钟的就先标记跳过不要在第一题上跟它死磕因为后面的编程题才是拉分项。我的习惯是选择题控制在40到50分钟内完成剩下的时间全部给编程题每道题先想清楚思路再动手不要想到哪写到哪。很多同学编程题写了一半发现思路错了回头改的时间成本远远高于一开始多花5分钟想清楚。这套策略在我后续参加其他公司的笔试里也验证过适合大部分线上考试场景。2. 算法题不止是LeetCode更看重边界思考2.1 高频考点数组、字符串、链表、二叉树从历年服务端实习笔试来看算法题的难度通常维持在LeetCode中等题水平很少出超难题但也不是简单的“反转字符串”那类送分题。最高频的几类考点集中在数组操作、字符串处理、链表题目、二叉树遍历。这个选择是有道理的。这几个数据结构最贴近日常业务开发尤其在服务端场景里列表数据、字符串处理、层级结构都是家常便饭。公司希望你具备的基本功是对常见数据结构的增删改查足够敏感能够在不同条件下选择合适的存储和遍历方式同时写出稳定的代码。我这里整理了一个备考优先级表格按照题量出现频率和性价比排序考点常见题型举例优先级别数组与双指针合并有序数组、三数之和、滑动窗口极高字符串最长回文子串、字符串转换、模式匹配高链表反转链表、快慢指针找环、合并K个有序链表高二叉树层序遍历、最近公共祖先、中序与后序重建高动态规划背包问题、最长上升子序列中高贪心算法区间调度、跳跃游戏中堆Top K问题、任务调度中2.2 一道典型的“小米风格”算法题解法我拿一道非常典型的题来做拆解反转链表。这道题你不会觉得难但面试官和笔试判题系统真正看重的是边界条件有没有处理完整。直接给一个迭代解法public ListNode reverseList(ListNode head) { ListNode prev null; ListNode curr head; while (curr ! null) { ListNode next curr.next; curr.next prev; prev curr; curr next; } return prev; }代码本身不难但很多人会在笔试现场写错两个地方一是循环终止条件写成curr.next ! null导致最后一个节点没有处理二是忘记保存curr.next导致指针丢失链表直接断掉。这种错误在本地IDE里不一定能及时发现但在OJ上就是编译运行直接报错或者死循环。笔试里更常见的变体是“反转链表的前N个节点”或者“每K个节点一组反转”。如果你连基础反转都能写利索这类变形题的核心思路是一样的就是先找到断点再继续递归或者迭代处理。建议准备时把所有链表反转相关变体都练一遍它们考的其实是一套逻辑。2.3 容易翻车的边界条件算法题最容易翻车的不是核心逻辑而是边界条件。比如数组为空、链表只有一个节点、字符串包含空格、输入数据量极大导致需要用long而不是int这些都是判题用例里必定会出现的坑。我见过太多同学核心思路全对因为没处理空指针异常整道题直接挂了。一个比较实用的习惯是写完代码后立刻在脑子里手动跑三个用例——空输入、单元素输入、大量输入。这三个用例能覆盖大部分常见的边界问题。对于数据规模题目一般会给提示比如数据量是10^5级别那你的算法复杂度必须是O(n log n)或者O(n)O(n^2)大概率超时。这个判断要形成肌肉记忆。一般线上OJ会给你构造测试用例的时间不要急着提交。先用自测用例跑一遍确认边界没问题再提交。如果平台支持多次提交也不代表最后只记录第一次分数但确实会影响心态所以尽量一遍过。3. Java基础与并发服务端开发的拦路虎3.1 Java集合框架的底层原理考察服务端开发岗位的笔试Java基础知识一定是重头戏。小米的招聘岗位描述里后端技术栈就是以Java为主所以对集合框架底层原理的考察几乎每年都有。最常考的几个点是HashMap的实现原理、ConcurrentHashMap的分段锁机制、ArrayList和LinkedList的区别、HashSet和TreeSet的底层。其中HashMap是必考题没有例外。你需要能答出来底层数据结构是数组加链表加红黑树默认初始容量是16负载因子是0.75当链表长度超过8且数组长度大于64时链表会转成红黑树扩容时元素会重新计算位置。很多人能背出这些结论但被问到“为什么要用红黑树而不是平衡二叉树”就答不上来了。红黑树的特点是牺牲了部分严格的平衡性换来了更少的旋转次数插入删除性能更稳定这个取舍思维在系统设计里也很常用。3.2 并发编程从synchronized到volatile并发编程是服务端开发绕不开的主题笔试也一定会涉及。我得提醒一句这块内容光看书是不够的你得真正动手写多线程程序才能体会到锁的竞争、可见性和原子性问题。笔试不会出那种极端的底层并发问题但一定会考基础概念和实际场景判断。我整理了几个高频考点volatile的可见性和禁止指令重排但不保证原子性synchronized的锁升级过程无锁、偏向锁、轻量级锁、重量级锁ThreadLocal的原理和使用场景以及内存泄漏问题线程池的核心参数corePoolSize、maximumPoolSize、keepAliveTime、workQueue、RejectedExecutionHandlerCountDownLatch和CyclicBarrier的区别举个最常考的判断题volatile修饰的变量在多线程环境下是线程安全的吗答案是否定的。volatile只能保证可见性和有序性不能保证原子性。比如多个线程同时对同一个volatile变量做count这样的操作最终结果依然可能不对。因为count本质上是三步读值、加一、写回volatile保证不了这三步的原子性。3.3 实战例子把并发问题讲清楚笔试的简答题经常给一个场景让你判断“这段代码有没有并发问题怎么修改”。我拿一个很典型的例子说明public class Counter { private int count 0; public void increment() { count; } public int getCount() { return count; } }这个类在单线程下没问题但在多线程环境下count不是原子操作多个线程同时调用increment()会导致数据丢失。有三种常见的修改方案一是给方法加synchronized关键字保证同一时刻只有一个线程执行该方法。二是用AtomicInteger替代int利用CAS机制保证原子性。三是用LongAdder在高并发场景下性能更好它把热点数据分散到多个单元上适合统计类的场景。这三种方案没有绝对的好坏取决于场景。面试官更想听的是“你能权衡利弊而不是只会背一种方案”。我在面试实习生时经常看到有人一说并发就会用synchronized但问到性能瓶颈和锁竞争优化就沉默了。真正加过分的人会告诉你synchronized在JDK 1.6之后做了大量优化很多场景下性能已经很好没有必要过度设计。笔试阶段能把基础方案选对并说清理由就已经胜过很多人了。4. 计算机网络与操作系统躲不掉的基础盘4.1 HTTP/TCP高频题从握手到状态码凡是“服务端开发工程师”的笔试计算机网络几乎是必考板块。这一块的考察非常标准化以至于你只要把几个经典问题吃透就能拿到大部分分数。最核心的考点包括TCP三次握手和四次挥手的过程及原因、TCP与UDP的区别、HTTP与HTTPS的区别、常见HTTP状态码的含义、输入一个URL后发生了什么。三次握手这道题除了要能画出流程更要理解为什么是三次而不是两次。核心原因是双方需要确认彼此的收发能力都正常第一次握手客户端确认服务端接收正常第二次握手服务端确认客户端发送正常客户端确认服务端发送正常第三次握手服务端确认客户端接收正常。少了任何一次都可能出现历史报文串扰或者资源浪费。状态码这块笔试很喜欢考的是301和302的区别401和403的区别500和502的区别。这里我建议背一个规律4开头是客户端问题5开头是服务端问题。301是永久重定向302是临时重定向401是未认证403是已认证但没有权限500是内部错误502是网关收到了无效响应。这个区分在排查线上问题时非常实用。4.2 进程线程与内存管理操作系统的选择题主要围绕进程与线程、死锁、内存管理、文件系统这几个方向。最常考的一个辨析是进程和线程的本质区别。这个不能只背“线程是进程的子集”要理解到资源分配和调度的层次进程是操作系统分配资源的基本单位线程是CPU调度的基本单位同一进程的多个线程共享进程的地址空间线程切换的开销比进程切换小得多。死锁产生的四个必要条件也是一个高频考点互斥、占有并等待、非抢占、循环等待。笔试通常以选择题形式出现给你一个场景判断是否可能产生死锁或者让你选择破坏循环等待条件的方案。实际开发中我们通常通过“给锁编号按固定顺序加锁”来避免死锁这个经验可以放在简答题的补充说明里。Linux基础偶尔也会出现在选择题里比如查看进程用ps -ef查看端口占用用netstat -tlnp监控系统资源用top。这些命令不复杂但确实属于服务端开发的基本功建议提前过一遍不要在这种送分题上丢分。4.3 数据库索引与事务隔离级别数据库在服务端笔试题中的占比一般是一个简答题加几道选择题。选择题比较容易考到索引失效的场景比如对索引列使用函数、隐式类型转换、左模糊查询等。简答题最容易考的是事务隔离级别和索引底层结构。事务隔离级别有四种读未提交、读已提交、可重复读、串行化。MySQL默认是可重复读这一点一定要记清楚容易被问“MySQL为什么默认用可重复读而不是读已提交”网上有很多解释核心是和主从复制有关在statement格式的binlog下使用读已提交会导致主从不一致所以MySQL官方把默认级别定为可重复读。这个点你要能讲出个一二三说明你真的是理解而不是背题。索引这块B树为什么适合做数据库索引也是一个经典问题。和B树相比B树的所有数据都存在叶子节点且叶子节点之间有指针连接这让范围查询非常高效。同时B树的非叶子节点只存索引不存数据一页能存储更多索引项树的高度更低磁盘IO次数更少。笔试如果能答到这一层基本就是满分答案了。5. 手写代码与设计题从“会做”到“会讲”5.1 单例模式懒汉、饿汉与双检锁小米的笔试题里手写代码题通常不会只考算法还会让你写一个设计模式或者简单的并发模型。我觉得这非常符合服务端开发的真实工作状态——你不仅要写业务代码还要保证代码写出来是线程安全的、可扩展的。单例模式是这里面最经典的题目。下面是我建议掌握的双检锁版本public class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance null) { synchronized (Singleton.class) { if (instance null) { instance new Singleton(); } } } return instance; } }这里有两个关键点值得说一下。第一个是synchronized里面还要再判断一次instance null因为可能有两个线程同时通过第一次判空都阻塞在同步代码块上如果第二次不判空还是会创建两个实例。第二个是volatile关键字这是很多人容易漏掉的。instance new Singleton()在字节码层面不是一个原子操作包含分配内存、初始化对象、把引用指向内存地址三步如果不加volatile指令重排后可能发生“分配内存-指向内存-初始化”的乱序导致另一个线程拿到一个未初始化完成的对象。5.2 手写生产者消费者模型另一个高频手写题是生产者消费者模型。这道题至少有三种写法用synchronized加wait/notify、用Lock加Condition、使用阻塞队列BlockingQueue。笔试时间有限我更推荐第三种代码最简洁也最不容易出错public class ProducerConsumer { private static final int CAPACITY 10; private static final BlockingQueueInteger queue new ArrayBlockingQueue(CAPACITY); static class Producer implements Runnable { Override public void run() { for (int i 0; i 100; i) { try { queue.put(i); System.out.println(生产: i); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } } static class Consumer implements Runnable { Override public void run() { while (true) { try { Integer val queue.take(); System.out.println(消费: val); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } } public static void main(String[] args) { new Thread(new Producer()).start(); new Thread(new Consumer()).start(); } }这个版本最核心的优点是阻塞队列本身就实现了锁和等待通知机制put在队列满时会阻塞take在队列空时会阻塞不需要你手动处理复杂的等待逻辑。笔试时写这个既快又不容易出bug。如果你要炫技可以再提一句用LinkedBlockingQueue或者SynchronousQueue的区别能加分。5.3 面试官角度代码规范与沟通表达从面试官的角度手写题看的不只是结果对不对还包括你的代码习惯和思考过程。就算这是机器判题的笔试你写出来的代码也要有自己的注释和清晰的命名因为后续面试官会看到你的解题代码这会直接影响第一印象。网上有个说法“大厂笔试代码写得乱面试官会觉得这个人做事没有章法。”这话有点夸张但确实有一定道理。一个简单的建议是变量命名用有意义的英文单词不要用a、b、c循环和条件分支能简洁就简洁但不要为了简洁牺牲可读性写完后花30秒扫一遍有没有无用代码和重复代码。还有一个容易被忽略的点如果笔试平台允许你写文字说明也就是在代码之外补充思路一定要写。比如“本题可以用贪心算法来解决时间复杂度O(n log n)”一行字就够了但能让面试官立刻明白你是有思路的而不是蒙出来的。这个习惯在面试手撕代码环节同样适用。6. 复盘笔试之后的下一步6.1 笔试完之后要做什么小米的实习生招聘流程一般是网申、在线笔试、技术面试、HR面试。笔试只是一个关卡真正决定你是否拿offer的是面试环节。所以笔试结束之后不管感觉好不好都建议立刻做一次复盘。把你考场上没答上来的题目记下来对应知识点整理出来逐一学习。我自己的经验是笔试复盘是最高效的学习方式因为你有了真实的场景记忆再去学这个知识点效率比平时漫无目的看书高好几倍。复盘的时候不要只看错题也要复盘时间分配。比如你发现选择题花了太长时间导致编程题没写完那下一次笔试就要调整策略。这种“考试节奏感”是需要刻意练习的。6.2 从笔试到面试的衔接准备如果笔试通过了面试官大概率会从你的笔试卷子里挑几道题来追问。特别是那些你做对了的题目反而要小心。我一个同学当年笔试时某道算法题写得特别漂亮面试官就在面试中让他再讲一遍然后不断追问“如果数据量扩大100倍怎么办”“如果改成海量数据怎么办”这种递进式追问非常考验真正理解的程度。所以笔试结束后的备考不能只看下一轮的内容。对于笔试题里涉及的每个知识点你都要准备一个“递进版本”这个方案有什么缺点数据量大怎么办这个方案的底层原理是什么把这些准备充分了面试翻车的概率会小很多。6.3 服务端技术栈的延伸思考最后再说点延伸的。无论笔试考的是什么小米服务端开发实习生日常要面对的都是真实的高并发场景电商秒杀、硬件设备大规模接入、消息推送、日志采集与分析。这些场景对技术能力的要求远不是笔试题目能覆盖完的。笔试只是确认你的底子真正拉开差距的是你对系统整体设计的理解。比如“秒杀系统怎么设计”这种系统设计题在校招里偶尔会出现需要你考虑流量削峰、接口防刷、库存扣减、缓存击穿等问题。我在实际做这类系统时常用的手段包括使用消息队列削峰、用Redis的Lua脚本保证库存扣减原子性、再加一层限流组件。这些思路和笔试里的并发题其实是连贯的底层逻辑是一样的。把这些串起来学习你的服务端技术体系才会完整对这些边界和深度的理解也会在面试里通过你的表达自然流露出来。我个人带实习生的经验是与其把精力花在搜集“往年真题”和“标准答案”上不如踏踏实实把每个基础模块背后的“为什么”搞清楚。笔试只是你技术积累的一次自然展现你的基础越扎实准备越充分题目怎么变都拦不住你。如果你现在还在备考阶段就把这份小米笔试当成一面镜子照出自己哪里还需要补然后一项一项去补齐。这条路没有任何捷径但走通之后你收获的绝不只是一个实习机会。
返回列表