Semaphore
大约 3 分钟Juc
作用
用于限制访问共享资源的线程上限
使用方法
//创建Semaphore对象,参数5代表最多允许5个线程访问共享资源,false代表非公平
Semaphore semaphore=new Semaphore(5,false);
new Thread(()->{
//在获取信号量的过程中有可能被打断,抛出异常,所以需要使用try-catch块包裹
try{
//尝试获取信号量
semaphore.acquire();
}catch(Exception e){
e.print();
}finally{
//释放信号量,让其它线程获取
semaphore.release();
}
}).start();Semaphore应用
适用于单机项目的限流,在访问高峰期使请求线程阻塞,主要是用于限制线程数量
这段话是在通过对比,阐述 Semaphore(信号量) 和 LimitLatch 在“限流”这个场景下,虽然原理相似,但限制的对象和适用场景有微妙的区别。
我来帮你“翻译”并深入拆解这段话的含义:
1. 核心观点解析
"Semaphore... 并且仅是限制线程数,而不是限制资源数 (例如连接数...)"
这句话是整个段落中最让人困惑的地方,因为我们通常认为线程和连接是一一对应的。但在高性能服务器(如 Tomcat NIO)中,它们不是一一对应的。
Semaphore 的逻辑:
Semaphore是 JDK 提供的工具,它直接挂起的是调用它的那个线程。- 如果你用 Semaphore 来限流,意味着:来一个请求 -> 分配一个线程 -> 线程去拿 Semaphore 许可 -> 拿不到就睡在这个线程里。
- 后果:如果有 10000 个请求并发进来,你可能瞬间消耗了 10000 个线程(虽然大部分在睡觉)。操作系统线程是非常昂贵的资源。
LimitLatch (Tomcat) 的逻辑:
LimitLatch限制的是 "资源数" (即 Socket 连接数 / Connection),而不是后端处理业务的线程数。- 在 NIO 模式下,一个线程(Poller)可以管理成千上万个连接。
- LimitLatch 是守在大门口(Acceptor)的。
- 流程:
- 外部来了 10000 个 TCP 连接请求。
LimitLatch说:“我只允许 200 个连接进来”。- Acceptor 线程只接收 200 个 Socket。
- 剩下的 9800 个连接请求,直接在操作系统内核的网络栈(TCP Backlog 队列)里排队,根本还没进入 Java 虚拟机,更没有分配 Java 线程。
"请对比 Tomcat LimitLatch 的实现"
作者在这里想表达的是: Tomcat 的 LimitLatch 虽然底层也是基于 AQS(像 Semaphore 一样),但它被放置的位置(架构层面)决定了它是为了保护连接资源,防止过多连接冲垮 Java 进程,而不仅仅是简单的“让线程排队”。
2. 举个生动的例子
场景:火锅店(服务器)
Semaphore 模式(限制线程):
- 你雇了 100 个服务员(线程)。
- 来了 100 个客人,每个服务员接待一个。
- 此时 Semaphore=10(只有 10 口锅)。
- 结果:10 个服务员带着客人在吃,剩下 90 个服务员带着客人在旁边干站着等。
- 问题:这 90 个服务员(线程)被占用了,工资照发(内存开销),但没干活。
LimitLatch 模式(限制资源/连接):
- 你在店门口放了个取号机(LimitLatch)。
- 虽然外面排了 1000 个人,但取号机只放 10 个人 进店。
- 店里只需要 10 个服务员 就能接待。
- 优势:外面那 990 个人是在门外(操作系统内核)等的,不占用店里(JVM)的空间和服务员(线程)。
3. 总结
这段话的意思是: 虽然 Semaphore 可以用来做限流,但它更侧重于在代码内部控制并发线程的执行;而在像 Tomcat 这种高性能网络服务器中,使用 LimitLatch 这种机制(在请求接入层进行控制)可以更高效地限制连接资源,避免为了维持“等待状态”而消耗大量宝贵的线程资源。