多线程项目场景:CountDownLatch、Future、Semaphore

面试官问"你们项目哪里用到了多线程",这不是让你背工具类定义。

他真正想判断的是:你有没有在真实业务里用多线程解决过吞吐、响应时间、资源限制这类问题。

PPT 里给了三个很适合面试回答的场景:

  1. ES 数据批量导入。
  2. 多接口数据汇总。
  3. 异步调用。

再加一个限流场景:Semaphore 控制某个方法允许并发访问的线程数量。

场景一:ES 数据批量导入

项目上线前,要把数据库里的文章数据同步到 ES 索引库。数据量可能是千万级。

最粗暴的写法是一次性把所有数据查出来,再批量写 ES。

这很容易 OOM。

更稳的做法是分页读取,每页固定数量,比如 2000 条,然后把每一页作为一个任务提交到线程池。主线程用 CountDownLatch 等所有分页任务完成。
#mermaid-svg-79ydoXuUk08bp419{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-79ydoXuUk08bp419 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-79ydoXuUk08bp419 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-79ydoXuUk08bp419 .error-icon{fill:#552222;}#mermaid-svg-79ydoXuUk08bp419 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-79ydoXuUk08bp419 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-79ydoXuUk08bp419 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-79ydoXuUk08bp419 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-79ydoXuUk08bp419 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-79ydoXuUk08bp419 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-79ydoXuUk08bp419 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-79ydoXuUk08bp419 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-79ydoXuUk08bp419 .marker.cross{stroke:#333333;}#mermaid-svg-79ydoXuUk08bp419 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-79ydoXuUk08bp419 p{margin:0;}#mermaid-svg-79ydoXuUk08bp419 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-79ydoXuUk08bp419 .cluster-label text{fill:#333;}#mermaid-svg-79ydoXuUk08bp419 .cluster-label span{color:#333;}#mermaid-svg-79ydoXuUk08bp419 .cluster-label span p{background-color:transparent;}#mermaid-svg-79ydoXuUk08bp419 .label text,#mermaid-svg-79ydoXuUk08bp419 span{fill:#333;color:#333;}#mermaid-svg-79ydoXuUk08bp419 .node rect,#mermaid-svg-79ydoXuUk08bp419 .node circle,#mermaid-svg-79ydoXuUk08bp419 .node ellipse,#mermaid-svg-79ydoXuUk08bp419 .node polygon,#mermaid-svg-79ydoXuUk08bp419 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-79ydoXuUk08bp419 .rough-node .label text,#mermaid-svg-79ydoXuUk08bp419 .node .label text,#mermaid-svg-79ydoXuUk08bp419 .image-shape .label,#mermaid-svg-79ydoXuUk08bp419 .icon-shape .label{text-anchor:middle;}#mermaid-svg-79ydoXuUk08bp419 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-79ydoXuUk08bp419 .rough-node .label,#mermaid-svg-79ydoXuUk08bp419 .node .label,#mermaid-svg-79ydoXuUk08bp419 .image-shape .label,#mermaid-svg-79ydoXuUk08bp419 .icon-shape .label{text-align:center;}#mermaid-svg-79ydoXuUk08bp419 .node.clickable{cursor:pointer;}#mermaid-svg-79ydoXuUk08bp419 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-79ydoXuUk08bp419 .arrowheadPath{fill:#333333;}#mermaid-svg-79ydoXuUk08bp419 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-79ydoXuUk08bp419 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-79ydoXuUk08bp419 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-79ydoXuUk08bp419 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-79ydoXuUk08bp419 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-79ydoXuUk08bp419 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-79ydoXuUk08bp419 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-79ydoXuUk08bp419 .cluster text{fill:#333;}#mermaid-svg-79ydoXuUk08bp419 .cluster span{color:#333;}#mermaid-svg-79ydoXuUk08bp419 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-79ydoXuUk08bp419 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-79ydoXuUk08bp419 rect.text{fill:none;stroke-width:0;}#mermaid-svg-79ydoXuUk08bp419 .icon-shape,#mermaid-svg-79ydoXuUk08bp419 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-79ydoXuUk08bp419 .icon-shape p,#mermaid-svg-79ydoXuUk08bp419 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-79ydoXuUk08bp419 .icon-shape .label rect,#mermaid-svg-79ydoXuUk08bp419 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-79ydoXuUk08bp419 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-79ydoXuUk08bp419 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-79ydoXuUk08bp419 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 查询 DB 总条数
计算总页数
创建 CountDownLatch 总页数
循环分页
分页查询文章数据
提交导入 ES 任务到线程池
批量写入 ES
countDown
主线程 await
全部导入完成

核心代码形态大概是:

java 复制代码
int pageSize = 2000;
int totalPage = calculateTotalPage(totalCount, pageSize);
CountDownLatch latch = new CountDownLatch(totalPage);

for (int page = 1; page <= totalPage; page++) {
    int currentPage = page;
    threadPool.submit(() -> {
        try {
            List<Article> articles = queryPage(currentPage, pageSize);
            bulkImportToEs(articles);
        } finally {
            latch.countDown();
        }
    });
}

latch.await();

这里 CountDownLatch 的作用不是提高单个任务速度,而是让主线程知道"所有子任务都结束了"。

CountDownLatch 怎么理解

CountDownLatch 可以理解成倒计时门闩。

构造时给一个计数值:

java 复制代码
CountDownLatch latch = new CountDownLatch(3);

其他线程每完成一个任务,就调用一次:

java 复制代码
latch.countDown();

等待方调用:

java 复制代码
latch.await();

当计数归零,等待方继续执行。
任务 3 任务 2 任务 1 主线程 任务 3 任务 2 任务 1 主线程 #mermaid-svg-JCj1llAKpZBw74Es{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JCj1llAKpZBw74Es .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JCj1llAKpZBw74Es .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JCj1llAKpZBw74Es .error-icon{fill:#552222;}#mermaid-svg-JCj1llAKpZBw74Es .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JCj1llAKpZBw74Es .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JCj1llAKpZBw74Es .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JCj1llAKpZBw74Es .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JCj1llAKpZBw74Es .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JCj1llAKpZBw74Es .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JCj1llAKpZBw74Es .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JCj1llAKpZBw74Es .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JCj1llAKpZBw74Es .marker.cross{stroke:#333333;}#mermaid-svg-JCj1llAKpZBw74Es svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JCj1llAKpZBw74Es p{margin:0;}#mermaid-svg-JCj1llAKpZBw74Es .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-JCj1llAKpZBw74Es text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-JCj1llAKpZBw74Es .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-JCj1llAKpZBw74Es .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-JCj1llAKpZBw74Es .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-JCj1llAKpZBw74Es .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-JCj1llAKpZBw74Es #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-JCj1llAKpZBw74Es .sequenceNumber{fill:white;}#mermaid-svg-JCj1llAKpZBw74Es #sequencenumber{fill:#333;}#mermaid-svg-JCj1llAKpZBw74Es #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-JCj1llAKpZBw74Es .messageText{fill:#333;stroke:none;}#mermaid-svg-JCj1llAKpZBw74Es .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-JCj1llAKpZBw74Es .labelText,#mermaid-svg-JCj1llAKpZBw74Es .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-JCj1llAKpZBw74Es .loopText,#mermaid-svg-JCj1llAKpZBw74Es .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-JCj1llAKpZBw74Es .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-JCj1llAKpZBw74Es .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-JCj1llAKpZBw74Es .noteText,#mermaid-svg-JCj1llAKpZBw74Es .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-JCj1llAKpZBw74Es .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-JCj1llAKpZBw74Es .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-JCj1llAKpZBw74Es .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-JCj1llAKpZBw74Es .actorPopupMenu{position:absolute;}#mermaid-svg-JCj1llAKpZBw74Es .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-JCj1llAKpZBw74Es .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-JCj1llAKpZBw74Es .actor-man circle,#mermaid-svg-JCj1llAKpZBw74Es line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-JCj1llAKpZBw74Es :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} await 等待 count = 3countDown count = 2countDown count = 1countDown count = 0继续执行

注意,CountDownLatch 是一次性的。计数归零后不能重置。如果要重复使用屏障,应该看 CyclicBarrier 或其他方案。

场景二:多接口数据汇总

电商订单详情页经常要查多块数据:

  1. 订单信息。
  2. 商品信息。
  3. 物流信息。

如果三个接口没有强依赖,串行调用就浪费时间。

假设订单信息 500ms,商品信息 800ms,物流信息 500ms。串行大约 1800ms,并行后耗时接近最慢的 800ms。
#mermaid-svg-aQCZTbvXcjeUqYe6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-aQCZTbvXcjeUqYe6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-aQCZTbvXcjeUqYe6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-aQCZTbvXcjeUqYe6 .error-icon{fill:#552222;}#mermaid-svg-aQCZTbvXcjeUqYe6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-aQCZTbvXcjeUqYe6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-aQCZTbvXcjeUqYe6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-aQCZTbvXcjeUqYe6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-aQCZTbvXcjeUqYe6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-aQCZTbvXcjeUqYe6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-aQCZTbvXcjeUqYe6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-aQCZTbvXcjeUqYe6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-aQCZTbvXcjeUqYe6 .marker.cross{stroke:#333333;}#mermaid-svg-aQCZTbvXcjeUqYe6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-aQCZTbvXcjeUqYe6 p{margin:0;}#mermaid-svg-aQCZTbvXcjeUqYe6 .mermaid-main-font{font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-aQCZTbvXcjeUqYe6 .exclude-range{fill:#eeeeee;}#mermaid-svg-aQCZTbvXcjeUqYe6 .section{stroke:none;opacity:0.2;}#mermaid-svg-aQCZTbvXcjeUqYe6 .section0{fill:rgba(102, 102, 255, 0.49);}#mermaid-svg-aQCZTbvXcjeUqYe6 .section2{fill:#fff400;}#mermaid-svg-aQCZTbvXcjeUqYe6 .section1,#mermaid-svg-aQCZTbvXcjeUqYe6 .section3{fill:white;opacity:0.2;}#mermaid-svg-aQCZTbvXcjeUqYe6 .sectionTitle0{fill:#333;}#mermaid-svg-aQCZTbvXcjeUqYe6 .sectionTitle1{fill:#333;}#mermaid-svg-aQCZTbvXcjeUqYe6 .sectionTitle2{fill:#333;}#mermaid-svg-aQCZTbvXcjeUqYe6 .sectionTitle3{fill:#333;}#mermaid-svg-aQCZTbvXcjeUqYe6 .sectionTitle{text-anchor:start;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-aQCZTbvXcjeUqYe6 .grid .tick{stroke:lightgrey;opacity:0.8;shape-rendering:crispEdges;}#mermaid-svg-aQCZTbvXcjeUqYe6 .grid .tick text{font-family:"trebuchet ms",verdana,arial,sans-serif;fill:#333;}#mermaid-svg-aQCZTbvXcjeUqYe6 .grid path{stroke-width:0;}#mermaid-svg-aQCZTbvXcjeUqYe6 .today{fill:none;stroke:red;stroke-width:2px;}#mermaid-svg-aQCZTbvXcjeUqYe6 .task{stroke-width:2;}#mermaid-svg-aQCZTbvXcjeUqYe6 .taskText{text-anchor:middle;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-aQCZTbvXcjeUqYe6 .taskTextOutsideRight{fill:black;text-anchor:start;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-aQCZTbvXcjeUqYe6 .taskTextOutsideLeft{fill:black;text-anchor:end;}#mermaid-svg-aQCZTbvXcjeUqYe6 .task.clickable{cursor:pointer;}#mermaid-svg-aQCZTbvXcjeUqYe6 .taskText.clickable{cursor:pointer;fill:#003163!important;font-weight:bold;}#mermaid-svg-aQCZTbvXcjeUqYe6 .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163!important;font-weight:bold;}#mermaid-svg-aQCZTbvXcjeUqYe6 .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163!important;font-weight:bold;}#mermaid-svg-aQCZTbvXcjeUqYe6 .taskText0,#mermaid-svg-aQCZTbvXcjeUqYe6 .taskText1,#mermaid-svg-aQCZTbvXcjeUqYe6 .taskText2,#mermaid-svg-aQCZTbvXcjeUqYe6 .taskText3{fill:white;}#mermaid-svg-aQCZTbvXcjeUqYe6 .task0,#mermaid-svg-aQCZTbvXcjeUqYe6 .task1,#mermaid-svg-aQCZTbvXcjeUqYe6 .task2,#mermaid-svg-aQCZTbvXcjeUqYe6 .task3{fill:#8a90dd;stroke:#534fbc;}#mermaid-svg-aQCZTbvXcjeUqYe6 .taskTextOutside0,#mermaid-svg-aQCZTbvXcjeUqYe6 .taskTextOutside2{fill:black;}#mermaid-svg-aQCZTbvXcjeUqYe6 .taskTextOutside1,#mermaid-svg-aQCZTbvXcjeUqYe6 .taskTextOutside3{fill:black;}#mermaid-svg-aQCZTbvXcjeUqYe6 .active0,#mermaid-svg-aQCZTbvXcjeUqYe6 .active1,#mermaid-svg-aQCZTbvXcjeUqYe6 .active2,#mermaid-svg-aQCZTbvXcjeUqYe6 .active3{fill:#bfc7ff;stroke:#534fbc;}#mermaid-svg-aQCZTbvXcjeUqYe6 .activeText0,#mermaid-svg-aQCZTbvXcjeUqYe6 .activeText1,#mermaid-svg-aQCZTbvXcjeUqYe6 .activeText2,#mermaid-svg-aQCZTbvXcjeUqYe6 .activeText3{fill:black!important;}#mermaid-svg-aQCZTbvXcjeUqYe6 .done0,#mermaid-svg-aQCZTbvXcjeUqYe6 .done1,#mermaid-svg-aQCZTbvXcjeUqYe6 .done2,#mermaid-svg-aQCZTbvXcjeUqYe6 .done3{stroke:grey;fill:lightgrey;stroke-width:2;}#mermaid-svg-aQCZTbvXcjeUqYe6 .doneText0,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneText1,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneText2,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneText3{fill:black!important;}#mermaid-svg-aQCZTbvXcjeUqYe6 .doneText0.taskTextOutsideLeft,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneText0.taskTextOutsideRight,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneText1.taskTextOutsideLeft,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneText1.taskTextOutsideRight,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneText2.taskTextOutsideLeft,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneText2.taskTextOutsideRight,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneText3.taskTextOutsideLeft,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneText3.taskTextOutsideRight{fill:black!important;}#mermaid-svg-aQCZTbvXcjeUqYe6 .crit0,#mermaid-svg-aQCZTbvXcjeUqYe6 .crit1,#mermaid-svg-aQCZTbvXcjeUqYe6 .crit2,#mermaid-svg-aQCZTbvXcjeUqYe6 .crit3{stroke:#ff8888;fill:red;stroke-width:2;}#mermaid-svg-aQCZTbvXcjeUqYe6 .activeCrit0,#mermaid-svg-aQCZTbvXcjeUqYe6 .activeCrit1,#mermaid-svg-aQCZTbvXcjeUqYe6 .activeCrit2,#mermaid-svg-aQCZTbvXcjeUqYe6 .activeCrit3{stroke:#ff8888;fill:#bfc7ff;stroke-width:2;}#mermaid-svg-aQCZTbvXcjeUqYe6 .doneCrit0,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneCrit1,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneCrit2,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneCrit3{stroke:#ff8888;fill:lightgrey;stroke-width:2;cursor:pointer;shape-rendering:crispEdges;}#mermaid-svg-aQCZTbvXcjeUqYe6 .milestone{transform:rotate(45deg) scale(0.8,0.8);}#mermaid-svg-aQCZTbvXcjeUqYe6 .milestoneText{font-style:italic;}#mermaid-svg-aQCZTbvXcjeUqYe6 .doneCritText0,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneCritText1,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneCritText2,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneCritText3{fill:black!important;}#mermaid-svg-aQCZTbvXcjeUqYe6 .doneCritText0.taskTextOutsideLeft,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneCritText0.taskTextOutsideRight,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneCritText1.taskTextOutsideLeft,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneCritText1.taskTextOutsideRight,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneCritText2.taskTextOutsideLeft,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneCritText2.taskTextOutsideRight,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneCritText3.taskTextOutsideLeft,#mermaid-svg-aQCZTbvXcjeUqYe6 .doneCritText3.taskTextOutsideRight{fill:black!important;}#mermaid-svg-aQCZTbvXcjeUqYe6 .vert{stroke:navy;}#mermaid-svg-aQCZTbvXcjeUqYe6 .vertText{font-size:15px;text-anchor:middle;fill:navy!important;}#mermaid-svg-aQCZTbvXcjeUqYe6 .activeCritText0,#mermaid-svg-aQCZTbvXcjeUqYe6 .activeCritText1,#mermaid-svg-aQCZTbvXcjeUqYe6 .activeCritText2,#mermaid-svg-aQCZTbvXcjeUqYe6 .activeCritText3{fill:black!important;}#mermaid-svg-aQCZTbvXcjeUqYe6 .titleText{text-anchor:middle;font-size:18px;fill:#333;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-aQCZTbvXcjeUqYe6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 000 000 000 000 000 000 000 000 000 000 000 000 000 000 订单信息 商品信息 物流信息 串行调用耗时
#mermaid-svg-zW06zy3Y5Mjb8QIa{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zW06zy3Y5Mjb8QIa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zW06zy3Y5Mjb8QIa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zW06zy3Y5Mjb8QIa .error-icon{fill:#552222;}#mermaid-svg-zW06zy3Y5Mjb8QIa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zW06zy3Y5Mjb8QIa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zW06zy3Y5Mjb8QIa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zW06zy3Y5Mjb8QIa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zW06zy3Y5Mjb8QIa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zW06zy3Y5Mjb8QIa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zW06zy3Y5Mjb8QIa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zW06zy3Y5Mjb8QIa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zW06zy3Y5Mjb8QIa .marker.cross{stroke:#333333;}#mermaid-svg-zW06zy3Y5Mjb8QIa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zW06zy3Y5Mjb8QIa p{margin:0;}#mermaid-svg-zW06zy3Y5Mjb8QIa .mermaid-main-font{font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-zW06zy3Y5Mjb8QIa .exclude-range{fill:#eeeeee;}#mermaid-svg-zW06zy3Y5Mjb8QIa .section{stroke:none;opacity:0.2;}#mermaid-svg-zW06zy3Y5Mjb8QIa .section0{fill:rgba(102, 102, 255, 0.49);}#mermaid-svg-zW06zy3Y5Mjb8QIa .section2{fill:#fff400;}#mermaid-svg-zW06zy3Y5Mjb8QIa .section1,#mermaid-svg-zW06zy3Y5Mjb8QIa .section3{fill:white;opacity:0.2;}#mermaid-svg-zW06zy3Y5Mjb8QIa .sectionTitle0{fill:#333;}#mermaid-svg-zW06zy3Y5Mjb8QIa .sectionTitle1{fill:#333;}#mermaid-svg-zW06zy3Y5Mjb8QIa .sectionTitle2{fill:#333;}#mermaid-svg-zW06zy3Y5Mjb8QIa .sectionTitle3{fill:#333;}#mermaid-svg-zW06zy3Y5Mjb8QIa .sectionTitle{text-anchor:start;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-zW06zy3Y5Mjb8QIa .grid .tick{stroke:lightgrey;opacity:0.8;shape-rendering:crispEdges;}#mermaid-svg-zW06zy3Y5Mjb8QIa .grid .tick text{font-family:"trebuchet ms",verdana,arial,sans-serif;fill:#333;}#mermaid-svg-zW06zy3Y5Mjb8QIa .grid path{stroke-width:0;}#mermaid-svg-zW06zy3Y5Mjb8QIa .today{fill:none;stroke:red;stroke-width:2px;}#mermaid-svg-zW06zy3Y5Mjb8QIa .task{stroke-width:2;}#mermaid-svg-zW06zy3Y5Mjb8QIa .taskText{text-anchor:middle;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-zW06zy3Y5Mjb8QIa .taskTextOutsideRight{fill:black;text-anchor:start;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-zW06zy3Y5Mjb8QIa .taskTextOutsideLeft{fill:black;text-anchor:end;}#mermaid-svg-zW06zy3Y5Mjb8QIa .task.clickable{cursor:pointer;}#mermaid-svg-zW06zy3Y5Mjb8QIa .taskText.clickable{cursor:pointer;fill:#003163!important;font-weight:bold;}#mermaid-svg-zW06zy3Y5Mjb8QIa .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163!important;font-weight:bold;}#mermaid-svg-zW06zy3Y5Mjb8QIa .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163!important;font-weight:bold;}#mermaid-svg-zW06zy3Y5Mjb8QIa .taskText0,#mermaid-svg-zW06zy3Y5Mjb8QIa .taskText1,#mermaid-svg-zW06zy3Y5Mjb8QIa .taskText2,#mermaid-svg-zW06zy3Y5Mjb8QIa .taskText3{fill:white;}#mermaid-svg-zW06zy3Y5Mjb8QIa .task0,#mermaid-svg-zW06zy3Y5Mjb8QIa .task1,#mermaid-svg-zW06zy3Y5Mjb8QIa .task2,#mermaid-svg-zW06zy3Y5Mjb8QIa .task3{fill:#8a90dd;stroke:#534fbc;}#mermaid-svg-zW06zy3Y5Mjb8QIa .taskTextOutside0,#mermaid-svg-zW06zy3Y5Mjb8QIa .taskTextOutside2{fill:black;}#mermaid-svg-zW06zy3Y5Mjb8QIa .taskTextOutside1,#mermaid-svg-zW06zy3Y5Mjb8QIa .taskTextOutside3{fill:black;}#mermaid-svg-zW06zy3Y5Mjb8QIa .active0,#mermaid-svg-zW06zy3Y5Mjb8QIa .active1,#mermaid-svg-zW06zy3Y5Mjb8QIa .active2,#mermaid-svg-zW06zy3Y5Mjb8QIa .active3{fill:#bfc7ff;stroke:#534fbc;}#mermaid-svg-zW06zy3Y5Mjb8QIa .activeText0,#mermaid-svg-zW06zy3Y5Mjb8QIa .activeText1,#mermaid-svg-zW06zy3Y5Mjb8QIa .activeText2,#mermaid-svg-zW06zy3Y5Mjb8QIa .activeText3{fill:black!important;}#mermaid-svg-zW06zy3Y5Mjb8QIa .done0,#mermaid-svg-zW06zy3Y5Mjb8QIa .done1,#mermaid-svg-zW06zy3Y5Mjb8QIa .done2,#mermaid-svg-zW06zy3Y5Mjb8QIa .done3{stroke:grey;fill:lightgrey;stroke-width:2;}#mermaid-svg-zW06zy3Y5Mjb8QIa .doneText0,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneText1,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneText2,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneText3{fill:black!important;}#mermaid-svg-zW06zy3Y5Mjb8QIa .doneText0.taskTextOutsideLeft,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneText0.taskTextOutsideRight,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneText1.taskTextOutsideLeft,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneText1.taskTextOutsideRight,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneText2.taskTextOutsideLeft,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneText2.taskTextOutsideRight,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneText3.taskTextOutsideLeft,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneText3.taskTextOutsideRight{fill:black!important;}#mermaid-svg-zW06zy3Y5Mjb8QIa .crit0,#mermaid-svg-zW06zy3Y5Mjb8QIa .crit1,#mermaid-svg-zW06zy3Y5Mjb8QIa .crit2,#mermaid-svg-zW06zy3Y5Mjb8QIa .crit3{stroke:#ff8888;fill:red;stroke-width:2;}#mermaid-svg-zW06zy3Y5Mjb8QIa .activeCrit0,#mermaid-svg-zW06zy3Y5Mjb8QIa .activeCrit1,#mermaid-svg-zW06zy3Y5Mjb8QIa .activeCrit2,#mermaid-svg-zW06zy3Y5Mjb8QIa .activeCrit3{stroke:#ff8888;fill:#bfc7ff;stroke-width:2;}#mermaid-svg-zW06zy3Y5Mjb8QIa .doneCrit0,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneCrit1,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneCrit2,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneCrit3{stroke:#ff8888;fill:lightgrey;stroke-width:2;cursor:pointer;shape-rendering:crispEdges;}#mermaid-svg-zW06zy3Y5Mjb8QIa .milestone{transform:rotate(45deg) scale(0.8,0.8);}#mermaid-svg-zW06zy3Y5Mjb8QIa .milestoneText{font-style:italic;}#mermaid-svg-zW06zy3Y5Mjb8QIa .doneCritText0,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneCritText1,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneCritText2,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneCritText3{fill:black!important;}#mermaid-svg-zW06zy3Y5Mjb8QIa .doneCritText0.taskTextOutsideLeft,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneCritText0.taskTextOutsideRight,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneCritText1.taskTextOutsideLeft,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneCritText1.taskTextOutsideRight,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneCritText2.taskTextOutsideLeft,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneCritText2.taskTextOutsideRight,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneCritText3.taskTextOutsideLeft,#mermaid-svg-zW06zy3Y5Mjb8QIa .doneCritText3.taskTextOutsideRight{fill:black!important;}#mermaid-svg-zW06zy3Y5Mjb8QIa .vert{stroke:navy;}#mermaid-svg-zW06zy3Y5Mjb8QIa .vertText{font-size:15px;text-anchor:middle;fill:navy!important;}#mermaid-svg-zW06zy3Y5Mjb8QIa .activeCritText0,#mermaid-svg-zW06zy3Y5Mjb8QIa .activeCritText1,#mermaid-svg-zW06zy3Y5Mjb8QIa .activeCritText2,#mermaid-svg-zW06zy3Y5Mjb8QIa .activeCritText3{fill:black!important;}#mermaid-svg-zW06zy3Y5Mjb8QIa .titleText{text-anchor:middle;font-size:18px;fill:#333;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-zW06zy3Y5Mjb8QIa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 000 000 000 000 000 000 000 000 000 000 000 000 000 000 订单信息 商品信息 物流信息 并行调用耗时

Future 的形态大概是:

java 复制代码
Future<Order> orderFuture = threadPool.submit(() -> queryOrder(orderId));
Future<List<Product>> productFuture = threadPool.submit(() -> queryProducts(orderId));
Future<Logistics> logisticsFuture = threadPool.submit(() -> queryLogistics(orderId));

OrderDetail detail = new OrderDetail();
detail.setOrder(orderFuture.get());
detail.setProducts(productFuture.get());
detail.setLogistics(logisticsFuture.get());

这类优化的前提是:几个任务之间没有强依赖。

如果商品查询必须依赖订单查询结果,那就不能盲目并行。

场景三:异步调用

有些操作不需要阻塞主流程,比如记录日志、发送通知、保存用户搜索记录。

主流程只需要快速返回,不需要等下游操作完成。这时可以把任务扔进线程池异步执行。
异步任务 线程池 业务服务 用户请求 异步任务 线程池 业务服务 用户请求 #mermaid-svg-HOndgytU71RgXGxq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HOndgytU71RgXGxq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HOndgytU71RgXGxq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HOndgytU71RgXGxq .error-icon{fill:#552222;}#mermaid-svg-HOndgytU71RgXGxq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HOndgytU71RgXGxq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HOndgytU71RgXGxq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HOndgytU71RgXGxq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HOndgytU71RgXGxq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HOndgytU71RgXGxq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HOndgytU71RgXGxq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HOndgytU71RgXGxq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HOndgytU71RgXGxq .marker.cross{stroke:#333333;}#mermaid-svg-HOndgytU71RgXGxq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HOndgytU71RgXGxq p{margin:0;}#mermaid-svg-HOndgytU71RgXGxq .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-HOndgytU71RgXGxq text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-HOndgytU71RgXGxq .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-HOndgytU71RgXGxq .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-HOndgytU71RgXGxq .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-HOndgytU71RgXGxq .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-HOndgytU71RgXGxq #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-HOndgytU71RgXGxq .sequenceNumber{fill:white;}#mermaid-svg-HOndgytU71RgXGxq #sequencenumber{fill:#333;}#mermaid-svg-HOndgytU71RgXGxq #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-HOndgytU71RgXGxq .messageText{fill:#333;stroke:none;}#mermaid-svg-HOndgytU71RgXGxq .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-HOndgytU71RgXGxq .labelText,#mermaid-svg-HOndgytU71RgXGxq .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-HOndgytU71RgXGxq .loopText,#mermaid-svg-HOndgytU71RgXGxq .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-HOndgytU71RgXGxq .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-HOndgytU71RgXGxq .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-HOndgytU71RgXGxq .noteText,#mermaid-svg-HOndgytU71RgXGxq .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-HOndgytU71RgXGxq .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-HOndgytU71RgXGxq .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-HOndgytU71RgXGxq .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-HOndgytU71RgXGxq .actorPopupMenu{position:absolute;}#mermaid-svg-HOndgytU71RgXGxq .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-HOndgytU71RgXGxq .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-HOndgytU71RgXGxq .actor-man circle,#mermaid-svg-HOndgytU71RgXGxq line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-HOndgytU71RgXGxq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 发起请求执行核心业务提交异步任务快速返回后台执行保存/通知/日志

但异步不是"随便丢后台"。至少要考虑:

  1. 任务失败怎么补偿。
  2. 队列满了怎么办。
  3. 线程池是否和主业务隔离。
  4. 是否需要 traceId、用户上下文传递。

面试里如果能主动说出这些边界,会比只说"我用了异步线程"可信很多。

Semaphore 控制并发访问数量

Semaphore 是信号量。它可以限制同时访问某个资源的线程数。

比如一个接口最多允许 3 个线程同时进入:

java 复制代码
Semaphore semaphore = new Semaphore(3);

public void handle() throws InterruptedException {
    semaphore.acquire();
    try {
        // 同一时刻最多 3 个线程执行这里
    } finally {
        semaphore.release();
    }
}

#mermaid-svg-GA9uS3M0L0lphBkj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-GA9uS3M0L0lphBkj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-GA9uS3M0L0lphBkj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-GA9uS3M0L0lphBkj .error-icon{fill:#552222;}#mermaid-svg-GA9uS3M0L0lphBkj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GA9uS3M0L0lphBkj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-GA9uS3M0L0lphBkj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GA9uS3M0L0lphBkj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GA9uS3M0L0lphBkj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-GA9uS3M0L0lphBkj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GA9uS3M0L0lphBkj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GA9uS3M0L0lphBkj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GA9uS3M0L0lphBkj .marker.cross{stroke:#333333;}#mermaid-svg-GA9uS3M0L0lphBkj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GA9uS3M0L0lphBkj p{margin:0;}#mermaid-svg-GA9uS3M0L0lphBkj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-GA9uS3M0L0lphBkj .cluster-label text{fill:#333;}#mermaid-svg-GA9uS3M0L0lphBkj .cluster-label span{color:#333;}#mermaid-svg-GA9uS3M0L0lphBkj .cluster-label span p{background-color:transparent;}#mermaid-svg-GA9uS3M0L0lphBkj .label text,#mermaid-svg-GA9uS3M0L0lphBkj span{fill:#333;color:#333;}#mermaid-svg-GA9uS3M0L0lphBkj .node rect,#mermaid-svg-GA9uS3M0L0lphBkj .node circle,#mermaid-svg-GA9uS3M0L0lphBkj .node ellipse,#mermaid-svg-GA9uS3M0L0lphBkj .node polygon,#mermaid-svg-GA9uS3M0L0lphBkj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-GA9uS3M0L0lphBkj .rough-node .label text,#mermaid-svg-GA9uS3M0L0lphBkj .node .label text,#mermaid-svg-GA9uS3M0L0lphBkj .image-shape .label,#mermaid-svg-GA9uS3M0L0lphBkj .icon-shape .label{text-anchor:middle;}#mermaid-svg-GA9uS3M0L0lphBkj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-GA9uS3M0L0lphBkj .rough-node .label,#mermaid-svg-GA9uS3M0L0lphBkj .node .label,#mermaid-svg-GA9uS3M0L0lphBkj .image-shape .label,#mermaid-svg-GA9uS3M0L0lphBkj .icon-shape .label{text-align:center;}#mermaid-svg-GA9uS3M0L0lphBkj .node.clickable{cursor:pointer;}#mermaid-svg-GA9uS3M0L0lphBkj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-GA9uS3M0L0lphBkj .arrowheadPath{fill:#333333;}#mermaid-svg-GA9uS3M0L0lphBkj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-GA9uS3M0L0lphBkj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-GA9uS3M0L0lphBkj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GA9uS3M0L0lphBkj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-GA9uS3M0L0lphBkj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GA9uS3M0L0lphBkj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-GA9uS3M0L0lphBkj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-GA9uS3M0L0lphBkj .cluster text{fill:#333;}#mermaid-svg-GA9uS3M0L0lphBkj .cluster span{color:#333;}#mermaid-svg-GA9uS3M0L0lphBkj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-GA9uS3M0L0lphBkj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-GA9uS3M0L0lphBkj rect.text{fill:none;stroke-width:0;}#mermaid-svg-GA9uS3M0L0lphBkj .icon-shape,#mermaid-svg-GA9uS3M0L0lphBkj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GA9uS3M0L0lphBkj .icon-shape p,#mermaid-svg-GA9uS3M0L0lphBkj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-GA9uS3M0L0lphBkj .icon-shape .label rect,#mermaid-svg-GA9uS3M0L0lphBkj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GA9uS3M0L0lphBkj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-GA9uS3M0L0lphBkj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-GA9uS3M0L0lphBkj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 10 个线程请求方法
Semaphore 许可数 = 3
3 个线程获得许可执行
其他线程等待
执行完成 release
等待线程继续 acquire

Semaphore 很适合资源数量明确的场景,比如:

  1. 限制同时访问某个第三方接口的请求数。
  2. 限制同时进行的文件上传数。
  3. 限制某个昂贵计算任务的并发数。

注意 release() 必须放在 finally,否则异常时许可不归还,系统会慢慢卡死。

这些场景怎么串成面试回答

不要一上来就报工具名。先讲业务问题,再讲为什么用多线程。

比如:

我们项目里有一个 ES 数据初始化场景,数据库里有千万级文章数据,不能一次性加载,否则容易 OOM。所以我做成分页查询,每页 2000 条,每页作为一个导入任务提交到线程池,主线程用 CountDownLatch 等全部分页任务完成。

还有订单详情页的数据汇总,订单、商品、物流来自不同服务,彼此没有依赖。原来串行调用耗时接近三者之和,后来用线程池加 Future 并行查询,整体耗时接近最慢的那个接口。

另外,一些不影响主流程的操作,比如保存搜索记录、写行为日志,可以放到异步线程池里执行,减少主接口响应时间。

如果某个资源有明确并发上限,比如第三方接口只能同时处理几个请求,可以用 Semaphore 控制并发访问数量。

这套回答听起来像真的做过,因为它不是在背 API,而是在讲问题、方案和边界。

相关推荐
小科先生2 小时前
初学者安装java
java·开发语言
wyhwust2 小时前
如何让maven帮我们去下载合适的包
java·maven
ID_180079054732 小时前
小红书笔记评论 API 接口深度解析(带全套 JSON 示例・技术实战版)
java·开发语言·windows
逍遥德2 小时前
Java编程高频的“技术点”-03:“下划线命名”参数,后端用“驼峰命名“接收
java·后端·springboot
jiayong232 小时前
Maven clean 报错与 Maven Profile 机制总结
java·maven
qq_366086222 小时前
测试接口传参数时,放在Header和Body中后台接收参数的区别
java·开发语言·前端
Jun6262 小时前
QT(8)-线程锁
java·开发语言
biubiubiu07062 小时前
SpringBoot 3.5.4 整合Quartz 定时任务
java·spring boot·spring
mifengxing2 小时前
LeetCode热题100——字母异位词分组
java·算法·leetcode·职场和发展·哈希表·hot100