Java多线程面试题:限制方法同时访问的线程数上限
实现限流器:控制最多n个线程同时访问
针对你遇到的这个面试问题,我先分享下我当时给出的手动实现方案,再聊聊更优雅的优化思路:
问题回顾
实现一个工具方法,同一时间最多允许n个线程访问;若第n+1个线程发起请求,需等待至已有线程完成。
手动实现的解决方案
我当时写的代码大概是这样的(补全了截断的部分):
class MyMathUtil implements Runnable { double a; double b; Thread t; // 静态变量记录当前并发数和最大允许数 static int currentCount = 0; static int maxCount = 2; MyMathUtil(double v1, double v2) { a = v1; b = v2; t = new Thread(this); t.start(); } @Override public void run() { try { // 进入临界区,判断是否达到并发上限 synchronized (MyMathUtil.class) { // 用while循环防止虚假唤醒 while (currentCount >= maxCount) { MyMathUtil.class.wait(); } currentCount++; } // 模拟实际业务操作:比如两数相加 System.out.println(Thread.currentThread().getName() + " 开始执行计算:" + a + " + " + b); Thread.sleep(2000); // 模拟耗时操作 double result = a + b; System.out.println(Thread.currentThread().getName() + " 计算完成,结果:" + result); } catch (InterruptedException e) { Thread.currentThread().interrupt(); e.printStackTrace(); } finally { // 执行完成后释放并发名额,唤醒等待的线程 synchronized (MyMathUtil.class) { currentCount--; MyMathUtil.class.notifyAll(); } } } // 测试入口 public static void main(String[] args) { // 启动5个线程,验证最多2个同时执行 for (int i = 0; i < 5; i++) { new MyMathUtil(i, i+1); } } }
实现逻辑说明
- 用静态变量
currentCount实时统计当前正在执行的线程数,maxCount设定最大并发阈值 - 通过
synchronized锁定类对象,保证计数操作的线程安全性 - 进入业务逻辑前,用
while循环(必须用while,不能用if,防止虚假唤醒)判断是否达到上限,达到则调用wait()进入等待状态 - 业务执行完成后,在
finally块中减少计数,并调用notifyAll()唤醒所有等待的线程,让它们竞争获取执行权限
更优雅的优化方案:使用JUC的Semaphore
其实Java并发包(java.util.concurrent)里已经提供了现成的工具类Semaphore,可以直接实现这个限流需求,不用手动维护计数和等待唤醒逻辑,代码更简洁可靠:
import java.util.concurrent.Semaphore; // 业务任务类,和限流逻辑解耦 class MathCalculationTask implements Runnable { private final double num1; private final double num2; private final Semaphore semaphore; public MathCalculationTask(double num1, double num2, Semaphore semaphore) { this.num1 = num1; this.num2 = num2; this.semaphore = semaphore; } @Override public void run() { try { // 获取许可:如果当前许可数为0,则线程阻塞等待 semaphore.acquire(); // 执行业务逻辑 System.out.println(Thread.currentThread().getName() + " 开始计算:" + num1 + " + " + num2); Thread.sleep(2000); double result = num1 + num2; System.out.println(Thread.currentThread().getName() + " 计算完成,结果:" + result); } catch (InterruptedException e) { Thread.currentThread().interrupt(); e.printStackTrace(); } finally { // 释放许可:将许可数加1,唤醒等待的线程 semaphore.release(); } } public static void main(String[] args) { // 设定最大并发数为2 int maxConcurrentThreads = 2; Semaphore semaphore = new Semaphore(maxConcurrentThreads); // 启动5个测试线程 for (int i = 0; i < 5; i++) { new Thread(new MathCalculationTask(i, i+1, semaphore)).start(); } } }
为什么推荐用Semaphore?
- 线程安全有保障:Semaphore是JDK官方实现的并发工具类,内部已经处理了所有线程安全问题,比手动实现的等待唤醒逻辑更可靠
- 解耦业务和限流:限流逻辑由Semaphore负责,业务类只需要专注于自己的计算逻辑,代码复用性更强
- 灵活配置:Semaphore支持公平/非公平模式(构造时传入
true开启公平模式),还能动态调整许可数,扩展性更好
内容的提问来源于stack exchange,提问作者ritesh
相关产品推荐
相关产品推荐

