面试官问“你们项目哪里用到了多线程”这不是让你背工具类定义。他真正想判断的是你有没有在真实业务里用多线程解决过吞吐、响应时间、资源限制这类问题。PPT 里给了三个很适合面试回答的场景ES 数据批量导入。多接口数据汇总。异步调用。再加一个限流场景Semaphore控制某个方法允许并发访问的线程数量。场景一ES 数据批量导入项目上线前要把数据库里的文章数据同步到 ES 索引库。数据量可能是千万级。最粗暴的写法是一次性把所有数据查出来再批量写 ES。这很容易 OOM。更稳的做法是分页读取每页固定数量比如 2000 条然后把每一页作为一个任务提交到线程池。主线程用CountDownLatch等所有分页任务完成。查询 DB 总条数计算总页数创建 CountDownLatch 总页数循环分页分页查询文章数据提交导入 ES 任务到线程池批量写入 EScountDown主线程 await全部导入完成核心代码形态大概是intpageSize2000;inttotalPagecalculateTotalPage(totalCount,pageSize);CountDownLatchlatchnewCountDownLatch(totalPage);for(intpage1;pagetotalPage;page){intcurrentPagepage;threadPool.submit(()-{try{ListArticlearticlesqueryPage(currentPage,pageSize);bulkImportToEs(articles);}finally{latch.countDown();}});}latch.await();这里CountDownLatch的作用不是提高单个任务速度而是让主线程知道“所有子任务都结束了”。CountDownLatch 怎么理解CountDownLatch可以理解成倒计时门闩。构造时给一个计数值CountDownLatchlatchnewCountDownLatch(3);其他线程每完成一个任务就调用一次latch.countDown();等待方调用latch.await();当计数归零等待方继续执行。任务 3任务 2任务 1主线程任务 3任务 2任务 1主线程await 等待 count 3countDown count 2countDown count 1countDown count 0继续执行注意CountDownLatch是一次性的。计数归零后不能重置。如果要重复使用屏障应该看CyclicBarrier或其他方案。场景二多接口数据汇总电商订单详情页经常要查多块数据订单信息。商品信息。物流信息。如果三个接口没有强依赖串行调用就浪费时间。假设订单信息 500ms商品信息 800ms物流信息 500ms。串行大约 1800ms并行后耗时接近最慢的 800ms。000000000000000000000000000000000000000000订单信息商品信息物流信息串行调用耗时000000000000000000000000000000000000000000订单信息商品信息物流信息并行调用耗时用Future的形态大概是FutureOrderorderFuturethreadPool.submit(()-queryOrder(orderId));FutureListProductproductFuturethreadPool.submit(()-queryProducts(orderId));FutureLogisticslogisticsFuturethreadPool.submit(()-queryLogistics(orderId));OrderDetaildetailnewOrderDetail();detail.setOrder(orderFuture.get());detail.setProducts(productFuture.get());detail.setLogistics(logisticsFuture.get());这类优化的前提是几个任务之间没有强依赖。如果商品查询必须依赖订单查询结果那就不能盲目并行。场景三异步调用有些操作不需要阻塞主流程比如记录日志、发送通知、保存用户搜索记录。主流程只需要快速返回不需要等下游操作完成。这时可以把任务扔进线程池异步执行。异步任务线程池业务服务用户请求异步任务线程池业务服务用户请求发起请求执行核心业务提交异步任务快速返回后台执行保存/通知/日志但异步不是“随便丢后台”。至少要考虑任务失败怎么补偿。队列满了怎么办。线程池是否和主业务隔离。是否需要 traceId、用户上下文传递。面试里如果能主动说出这些边界会比只说“我用了异步线程”可信很多。Semaphore 控制并发访问数量Semaphore是信号量。它可以限制同时访问某个资源的线程数。比如一个接口最多允许 3 个线程同时进入SemaphoresemaphorenewSemaphore(3);publicvoidhandle()throwsInterruptedException{semaphore.acquire();try{// 同一时刻最多 3 个线程执行这里}finally{semaphore.release();}}10 个线程请求方法Semaphore 许可数 33 个线程获得许可执行其他线程等待执行完成 release等待线程继续 acquireSemaphore很适合资源数量明确的场景比如限制同时访问某个第三方接口的请求数。限制同时进行的文件上传数。限制某个昂贵计算任务的并发数。注意release()必须放在finally否则异常时许可不归还系统会慢慢卡死。这些场景怎么串成面试回答不要一上来就报工具名。先讲业务问题再讲为什么用多线程。比如我们项目里有一个 ES 数据初始化场景数据库里有千万级文章数据不能一次性加载否则容易 OOM。所以我做成分页查询每页 2000 条每页作为一个导入任务提交到线程池主线程用CountDownLatch等全部分页任务完成。还有订单详情页的数据汇总订单、商品、物流来自不同服务彼此没有依赖。原来串行调用耗时接近三者之和后来用线程池加Future并行查询整体耗时接近最慢的那个接口。另外一些不影响主流程的操作比如保存搜索记录、写行为日志可以放到异步线程池里执行减少主接口响应时间。如果某个资源有明确并发上限比如第三方接口只能同时处理几个请求可以用Semaphore控制并发访问数量。这套回答听起来像真的做过因为它不是在背 API而是在讲问题、方案和边界。