锁 - 信号量Semaphore
Semaphore 直译:发出信号、打旗语,在编程中通常被称为:计数信号量,或者信号量。
举个生活案例
- 比如说旅店有10个单人间,有非常多的人要住;
- 最多只会只有10个人拿到钥匙;
- 没拿到钥匙的人非要住,那就只能等了;
- 而拿走钥匙的人,不住了那就必须退还钥匙,不然别人就没办法住。
信号量的使用类似于案例中的钥匙,它可以给任意的东西配一个使用许可,代码内部维护了一个许可集,当线程希望执行某个操作,就必须先获取许可,如有许可集已经空了,在许可可用前,后面申请许可的线程都会进入等待。
Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。
应用场景举例:读取压缩文件,有多个线程,都想读取这个文件,但是解压步骤,只能一个线程执行。
简单的代码展示
public class Test {
private static Semaphore available = new Semaphore(5);
public static void doSomething(){
// 这里信号量限制为5,最多只能有5个线程访问,多余的将被阻塞
available.acquireUninterruptibly();
//TODO : 执行别的事情
//释放信号量
available.release();
}
}
使用信号量处理线程同步的操作
业务场景:主线程需要5个并发的初始化操作,必须等5个线程全部初始化完毕,主线程继续执行
这个问题解决方案多种多样,信号量、倒数锁、屏障锁都可以做,直接使用同步块也能解决。这里展示信号量的解决方式,代码相对简单。
/**
* 主线程需要5个并发的初始化操作,5个线程全部初始化完毕,主线程开始执行
* @author ChenSS on 2018年2月3日
*/
public class Test {
private static Semaphore available = new Semaphore(5);
public static void main(String[] args) throws InterruptedException {
int count = 5;
// 一次性申请全部信号量
available.acquireUninterruptibly(5);
while (count-- > 0) {
final int n = count;
new Thread(() -> {
try {
Thread.sleep(3000 + RandomUtils.nextLong(3000));
System.out.println("完成第" + n + "个步骤");
available.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
// 再次申请全部信号量,在所有线程归还信号量之前,线程阻塞
available.acquireUninterruptibly(5);
// 引用置空
available = null;
System.out.println("五个初始化操作成功,进行别的操作!");
}
}
官方Demo
/**
* 官方Demo(类似于连接池):
* 开始的时候初始化一些元素
* 每次取出元素前,申请1个信号量,回收元素的时候,归还1个信号量。
* 当全部元素被取出,下一次取出元素的操作将被阻塞,直到有信号量被归还
*
* @author ChenSS on 2018年2月3日
*/
class Pool {
private static final int MAX_AVAILABLE = 3;
private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
//不是十分有效的存储结构
protected Object[] items = { "aaaa", "bbbb", "cccc" };
protected boolean[] used = new boolean[MAX_AVAILABLE];
public Object getItem() throws InterruptedException {
available.acquire();
return getNextAvailableItem();
}
public void putItem(Object x) {
if (markAsUnused(x))
available.release();
}
protected synchronized Object getNextAvailableItem() {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (!used[i]) {
used[i] = true;
return items[i];
}
}
return null;
}
protected synchronized boolean markAsUnused(Object item) {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (item == items[i]) {
if (used[i]) {
used[i] = false;
return true;
} else
return false;
}
}
return false;
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
Pool pool = new Pool();
int count = 5;
while (count-- > 0) {
if (count < 2){
//回收元素
pool.putItem("aaaa");
}
System.out.println(pool.getItem());
}
}
}
疯狂的妞妞 :每一天,做什么都好,不要什么都不做!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY