ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Java虚拟线程(二)

2026/10/2 8:31:05 拓冰建站 浏览量
Java虚拟线程(二) 虚拟线程 Semaphore 切面封装Spring AOP需求统一控制下游 RPC/DB 的并发配额避免虚拟线程海量并发打垮依赖服务不用每个业务方法手动写acquire/release注解驱动。适用SpringBoot3.2 JDK21虚拟线程环境1. 自定义注解import java.lang.annotation.*; Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) Documented public interface VtSemaphoreLimit { /** * 信号量最大许可数 */ int value(); /** * 资源名称用于监控日志区分不同下游 */ String resource(); /** * 获取许可超时时间单位毫秒0无限等待 */ long waitTimeout() default 0; }2. AOP 切面实现import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; Slf4j Aspect Component public class VtSemaphoreAspect { // key:资源名value:信号量实例 private final MapString, Semaphore semaphoreMap new ConcurrentHashMap(); Pointcut(annotation(vtSemaphoreLimit)) public void pointCut(VtSemaphoreLimit vtSemaphoreLimit) { } Around(pointCut(vtSemaphoreLimit)) public Object around(ProceedingJoinPoint joinPoint, VtSemaphoreLimit vtSemaphoreLimit) throws Throwable { String resource vtSemaphoreLimit.resource(); int permits vtSemaphoreLimit.value(); long timeout vtSemaphoreLimit.waitTimeout(); // 按资源名懒加载创建信号量 Semaphore semaphore semaphoreMap.computeIfAbsent(resource, k - new Semaphore(permits)); boolean acquired; if (timeout 0) { semaphore.acquire(); acquired true; } else { acquired semaphore.tryAcquire(timeout, TimeUnit.MILLISECONDS); } if (!acquired) { log.warn(资源[{}]获取信号量许可超时最大并发:{}, resource, permits); throw new RuntimeException(下游资源并发超限 resource); } try { // 执行目标方法DB/RPC调用 return joinPoint.proceed(); } finally { semaphore.release(); log.debug(资源[{}]释放许可剩余可用:{}, resource, semaphore.availablePermits()); } } }3. 使用示例业务 Serviceimport org.springframework.stereotype.Service; Service public class RemoteCallService { /** * 限制调用user-rpc最多同时20个并发 */ VtSemaphoreLimit(value 20, resource user-rpc, waitTimeout 3000) public String callUserRpc() { // 远程RPC调用IO阻塞 return rpc result; } /** * 限制订单DB查询最多同时30个并发 */ VtSemaphoreLimit(value 30, resource order-db, waitTimeout 5000) public Object queryOrderDb() { // db查询 return null; } }4. 配套监控增强可选生产推荐把信号量的等待队列、可用许可埋入 Micrometer 指标接入 Prometheus// 在切面增加注册metric示例 // meterRegistry.gauge(vt.semaphore.available, semaphore, Semaphore::availablePermits); // meterRegistry.gauge(vt.semaphore.queue_length, semaphore, s - s.getQueueLength());监控指标vt.semaphore.availablePermits剩余许可vt.semaphore.queueLength等待中的虚拟线程数量queueLength 持续上涨 → 下游慢需要告警5. 重点注意事项虚拟线程环境特有Semaphore 本身是兼容虚拟线程的Semaphore的acquire在虚拟线程阻塞时可以释放载体线程不会像synchronized那样卡住 OS 载体线程。✅安全。超时waitTimeout建议配置 无限等待acquire()在下游雪崩时会堆积大量虚拟线程在等待最好加超时保护快速失败。信号量粒度建议按下游服务 / 数据库划分不要按接口划分 同一个 RPC 服务多个接口复用同一个信号量防止流量不均衡。不要用这个信号量替代 DB 连接池 信号量是应用层限流DB 连接池是数据库侧连接管控两者是双层防护缺一不可。6. 扩展思路可选支持动态配置把并发数放到 Nacos/Apollo 配置中心不用重启服务修改 permits降级策略获取许可失败时直接返回降级结果而不是抛异常链路追踪获取 / 释放许可时埋点记录等待耗时7. 总结虚拟线程虽然线程创建廉价但无法保护下游依赖所以通过 AOP 自定义注解封装 Semaphore做应用层并发控制Semaphore 阻塞可以卸载载体线程对虚拟线程友好按下游资源隔离并发配额增加超时和监控实现双层防护应用层信号量 DB 连接池。