网站开发客户来源wordpress调用文章排序
2026/1/23 22:11:46 网站建设 项目流程
网站开发客户来源,wordpress调用文章排序,本溪 网站建设 做网站,网站开发文档范文文章目录并发系列#xff08;一#xff09;#xff1a;深入理解信号量#xff08;含 Redis 分布式信号量#xff09;一、信号量是什么#xff1f;二、信号量的典型使用场景1. 控制并发访问数量2. 限制资源#xff08;连接、对象#xff09;的最大使用数量3. 实现简单对…文章目录并发系列一深入理解信号量含 Redis 分布式信号量一、信号量是什么二、信号量的典型使用场景1. 控制并发访问数量2. 限制资源连接、对象的最大使用数量3. 实现简单对象池 / 连接池控制4. 实现“并发维度”的限流5. 模拟现实世界资源车位 / 座位 / 号牌三、本地信号量工具与代码示例Java3.1 Semaphore标准计数信号量示例限制任务的同时执行数量3.2 CountDownLatch一次性“倒计时信号量”示例主线程等待多个子任务执行完毕3.3 CyclicBarrier可循环使用的同步栅栏示例多个线程分阶段同步执行四、Redis 信号量分布式场景下的并发控制4.1 Redis 信号量是什么4.2 Redis 信号量的特别之处4.3 Redis 信号量的典型使用场景五、基于 Redisson 的 Redis 分布式信号量代码示例5.1 Redisson 配置示例5.2 使用 RSemaphore 实现接口级并发限流六、总结往期技术资料分享速通车并发系列一深入理解信号量含 Redis 分布式信号量一、信号量是什么在并发编程中信号量Semaphore是一种非常经典的同步原语用于控制同时访问某个共享资源的线程数量。可以把信号量想象成一个“带计数器的锁”内部维护一个计数器许可数permits每个线程在访问受保护资源前必须先从信号量中“申请一个许可”如果还有剩余许可计数器减 1线程可以继续执行如果许可已经耗尽后续线程就需要等待直到有线程释放许可计数器加 1。在 Java 中最典型的实现就是java.util.concurrent.Semaphore。在分布式场景中我们常用Redis 实现分布式信号量来做跨进程、跨机器的并发控制。acquire() 占用 1 个名额acquire() 占用 1 个名额acquire() 占用 1 个名额acquire() 发现无名额阻塞等待信号量permits 3当前可用许可 3线程 A线程 B线程 C线程 D二、信号量的典型使用场景1. 控制并发访问数量某个接口、某段逻辑最多只能允许 N 个线程同时执行防止压垮下游服务超过 N 的请求要么排队等待要么快速失败返回“稍后重试”。2. 限制资源连接、对象的最大使用数量只有 10 条数据库连接、20 个对象实例等希望同时占用数量不能超过上限使用信号量来“发放名额”获得许可才能从池子里借资源用完后释放。3. 实现简单对象池 / 连接池控制借出前acquire()归还时release()确保池中“借出中的数量”不超过初始化设置与阻塞队列一起使用时可以同时控制“任务排队长度”和“并发执行数量”。4. 实现“并发维度”的限流常见限流维度有 QPS每秒请求数、并发数同时处理中的请求数信号量天然适合做“最大并发数 N”的限流避免服务被瞬时高并发拖垮。5. 模拟现实世界资源车位 / 座位 / 号牌地下停车场有 100 个车位每进来一辆车就占用一个车位没有车位就必须等待影院有 200 个座位只能卖 200 张票多了就要拒绝这些场景都可以用信号量来抽象和建模。三、本地信号量工具与代码示例Java本小节先看“单机单 JVM”场景下的信号量工具主要是 Java 并发包中的几个常用类Semaphore标准的计数信号量CountDownLatch一次性“倒计时信号量”CyclicBarrier可循环使用的“阶段性同步栅栏”。3.1Semaphore标准计数信号量Semaphore是 Java 并发包java.util.concurrent提供的计数信号量实现用于控制同时访问某资源的线程数量。典型构造方式// permits: 允许同时访问的线程数SemaphoresemaphorenewSemaphore(intpermits);// fair: 是否公平先来先得默认为非公平SemaphorefairSemaphorenewSemaphore(3,true);示例限制任务的同时执行数量importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.Semaphore;publicclassSemaphoreDemo{// 最多允许 3 个线程并发执行privatestaticfinalSemaphoreSEMAPHOREnewSemaphore(3);publicstaticvoidmain(String[]args){ExecutorServiceexecutorServiceExecutors.newFixedThreadPool(10);for(inti0;i10;i){inttaskIdi;executorService.submit(()-{try{// 获取许可如果没有可用许可会阻塞等待SEMAPHORE.acquire();System.out.println(任务 taskId 获取许可开始执行当前线程Thread.currentThread().getName());Thread.sleep(2000);// 模拟业务处理System.out.println(任务 taskId 执行完成准备释放许可);}catch(InterruptedExceptione){Thread.currentThread().interrupt();}finally{// 无论如何都要释放SEMAPHORE.release();}});}executorService.shutdown();}}要点acquire()阻塞式获取许可没有许可时会挂起当前线程tryAcquire()/tryAcquire(timeout, unit)非阻塞或带超时获取许可适合做“快速失败”或“有限等待”release()释放许可通常放在finally中避免异常导致“占坑不还”。线程Semaphore(permits1)临界区资源acquire()成功permits--进入临界区执行逻辑执行完毕release()permits阻塞等待或超时返回alt[有可用许可][没有可用许可]线程Semaphore(permits1)临界区资源3.2CountDownLatch一次性“倒计时信号量”CountDownLatch通过一个初始计数值来控制线程之间的等待关系初始化时设置一个正整数 N每当某个任务完成时调用countDown()计数减 1当计数减到 0 时所有在await()处等待的线程会被同时唤醒。它不是传统意义上的“可重复使用的信号量”但同样基于计数机制来实现线程协作。示例主线程等待多个子任务执行完毕importjava.util.concurrent.CountDownLatch;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;publicclassCountDownLatchDemo{privatestaticfinalintTASK_COUNT3;publicstaticvoidmain(String[]args)throwsInterruptedException{CountDownLatchlatchnewCountDownLatch(TASK_COUNT);ExecutorServiceexecutorServiceExecutors.newFixedThreadPool(TASK_COUNT);for(inti0;iTASK_COUNT;i){inttaskIdi;executorService.submit(()-{try{System.out.println(子任务 taskId 开始执行...);Thread.sleep(1000taskId*500L);System.out.println(子任务 taskId 执行完成);}catch(InterruptedExceptione){Thread.currentThread().interrupt();}finally{// 完成一个任务计数减一latch.countDown();}});}System.out.println(主线程等待所有子任务完成...);// 当计数归零时await 解除阻塞latch.await();System.out.println(所有子任务完成主线程继续执行);executorService.shutdown();}}特点适合“一次性”的协作例如系统启动时等待多个模块初始化完成计数归零后就不能重置因此不能循环使用。3.3CyclicBarrier可循环使用的同步栅栏CyclicBarrier更像是“可重复使用的栅栏”适合多线程在某个阶段全部到齐后再一起进入下一阶段。构造时指定参与线程数 N每个线程在某个阶段完成后调用await()等待其他线程当第 N 个线程调用await()时所有线程同时被唤醒继续执行栅栏可重用可以进入下一轮阶段同步。示例多个线程分阶段同步执行importjava.util.concurrent.BrokenBarrierException;importjava.util.concurrent.CyclicBarrier;publicclassCyclicBarrierDemo{privatestaticfinalintTHREAD_COUNT3;publicstaticvoidmain(String[]args){// 所有线程都到达屏障点后会执行 barrierActionCyclicBarrierbarriernewCyclicBarrier(THREAD_COUNT,()-System.out.println(所有线程到达栅栏点开始下一阶段...));for(inti0;iTHREAD_COUNT;i){intworkerIdi;ThreadworkernewThread(()-{try{System.out.println(线程 workerId 执行第一阶段任务);Thread.sleep(1000workerId*500L);System.out.println(线程 workerId 第一阶段完成等待其他线程...);barrier.await();// 等待所有线程到达这里System.out.println(线程 workerId 开始第二阶段任务);Thread.sleep(1000workerId*500L);System.out.println(线程 workerId 第二阶段完成);}catch(InterruptedExceptione){Thread.currentThread().interrupt();}catch(BrokenBarrierExceptione){System.out.println(栅栏被打破e.getMessage());}});worker.start();}}}特点适合“多线程分阶段协同”的场景比如并行计算中的分步汇总与CountDownLatch相比CyclicBarrier可以多次复用。四、Redis 信号量分布式场景下的并发控制上面的信号量工具都属于“本地单 JVM”工具只能控制当前进程内部线程的并发访问。在微服务、分布式系统中我们更常遇到这样的需求多个应用实例部署在不同机器上所有实例访问同一个第三方接口 / 下游服务希望对整个集群设置“最大并发数”的上限比如全局最多 50 个请求同时打到第三方接口。这时单机Semaphore已经不够用了需要一个分布式信号量而 Redis 就是一个非常合适的“共享状态存储”。4.1 Redis 信号量是什么Redis 本身并没有内置Semaphore类型但我们可以基于 Redis 的原子自增 / 自减INCR/DECRLua 脚本有序集合 / 列表等数据结构来实现一个跨进程、跨机器共享的计数器从而达到“分布式信号量”的效果。很多 Redis 客户端框架例如 Redisson已经封装好了这套逻辑提供了类似RSemaphore的抽象对使用者来说与本地Semaphore非常相似。4.2 Redis 信号量的特别之处与本地Semaphore相比Redis 信号量有几个非常重要的特点作用范围不同本地Semaphore只在当前 JVM 内部有效Redis 信号量基于 Redis 存储天然支持多实例、多机器之间共享适合作为全局并发配额控制工具。阻塞模型不同本地Semaphore.acquire()可以直接阻塞当前线程等待许可释放Redis 是网络服务本身不会“挂起”调用方线程通常通过“轮询 睡眠”或封装在客户端如 Redisson内部来实现等待逻辑。需要考虑“宕机 / 超时”场景本地环境下如果线程异常终止一般很快能感知分布式环境中某个实例拿到许可后如果宕机或者网络异常可能永远不会主动释放许可因此 Redis 信号量通常要配合过期时间TTL或租约lease机制防止“占坑不还”导致整体可用许可数越来越少最终被锁死。一致性与容错更复杂需要考虑主从复制延迟、Redis 宕机、主从切换、客户端重试等情况一般使用 Lua 脚本将“检查 修改”操作打包成原子操作保证一致性。总体来说本地信号量控制的是“一个进程内的线程并发”Redis 信号量控制的是“多个进程 / 多台机器之间的全局并发”。4.3 Redis 信号量的典型使用场景分布式接口限流按并发数整个集群对某个接口、某类操作最多允许 N 个请求同时在执行超出的请求要么排队等待要么直接返回“稍后重试”。分布式任务调度的全局“工作线程”上限多个 worker 实例从队列中消费任务希望集群内同时执行的任务数不能超过 M。跨语言 / 跨技术栈共享资源配额Java、Go、Python 等不同语言的服务共同访问同一个下游 API通过 Redis 的分布式信号量共享同一套“名额池”。五、基于 Redisson 的 Redis 分布式信号量代码示例下面以 Spring Boot Redisson 为例演示如何使用 Redis 实现全局并发限制。5.1 Redisson 配置示例首先定义一个 Redisson 客户端配置用于连接 Redisimportorg.redisson.Redisson;importorg.redisson.api.RedissonClient;importorg.redisson.config.Config;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;ConfigurationpublicclassRedissonConfig{BeanpublicRedissonClientredissonClient(){ConfigconfignewConfig();// 单节点示例生产环境可以使用哨兵 / 集群配置config.useSingleServer().setAddress(redis://127.0.0.1:6379).setDatabase(0);returnRedisson.create(config);}}5.2 使用RSemaphore实现接口级并发限流假设我们有一个接口/redisSemaphore/doWork要求整个集群层面最多只能有 5 个请求同时在执行超出并发限制的请求等待 1 秒还拿不到名额则快速失败防止长时间阻塞。importjava.util.concurrent.TimeUnit;importorg.redisson.api.RSemaphore;importorg.redisson.api.RedissonClient;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RestController;RestControllerpublicclassRedisSemaphoreController{privatefinalRedissonClientredissonClient;publicRedisSemaphoreController(RedissonClientredissonClient){this.redissonClientredissonClient;}GetMapping(/redisSemaphore/doWork)publicStringdoWork()throwsInterruptedException{// 获取一个分布式信号量对象key 可以根据业务命名RSemaphoresemaphoreredissonClient.getSemaphore(demo:semaphore:doWork);// 初始化许可数只在第一次时需要通常可以在应用启动阶段设置好semaphore.trySetPermits(5);// 尝试在 1 秒内获取一个许可获取不到则快速失败booleanacquiredsemaphore.tryAcquire(1,TimeUnit.SECONDS);if(!acquired){return当前请求过多请稍后重试;}try{// 模拟业务逻辑Thread.sleep(2000L);return处理成功线程Thread.currentThread().getName();}finally{// 处理完成后归还许可semaphore.release();}}}示例说明getSemaphore(demo:semaphore:doWork)所有应用实例只要使用同一个 key就共享同一批“并发名额”trySetPermits(5)将最大并发许可数设置为 5一般在应用初始化时设置一次即可tryAcquire(1, TimeUnit.SECONDS)等待最多 1 秒尝试获取许可获取成功则进入业务处理获取失败则直接返回友好提示release()放在finally中确保无论业务是否异常结束名额都能被归还。与本地Semaphore相比差异在于信号量的状态存储在 Redis 中而不是单个 JVM 内存中所有服务实例共享同一“名额池”实现真正的全局并发限制。客户端请求应用实例Redis RSemaphore下游服务/第三方接口HTTP 请求 /redisSemaphore/doWorktryAcquire(1s)返回成功(获得许可)调用下游服务响应结果release()业务处理成功超时 / 无名额当前请求过多请稍后重试alt[获取成功][获取失败]客户端请求应用实例Redis RSemaphore下游服务/第三方接口六、总结**信号量Semaphore**是控制并发访问数量的核心工具可以看作“带计数器的锁”在 Java 中Semaphore、CountDownLatch、CyclicBarrier等工具可以很好地解决单机多线程场景下的同步与并发控制问题在分布式系统中单机信号量已经不够需要依赖 Redis 这类中间件实现分布式信号量典型场景包括分布式接口并发限流分布式任务调度的全局并发数控制跨语言 / 跨应用共享同一资源配额基于 Redisson 的RSemaphore我们可以以几乎与本地Semaphore相同的编码方式在 Spring Boot 中轻松落地 Redis 分布式信号量。在实际工程中推荐将本地信号量 Redis 信号量 线程池 阻塞队列结合使用本地信号量限制单机并发保护本机资源CPU、内存、线程数Redis 信号量限制集群整体并发保护下游服务线程池和队列负责排队与调度从而构建一套既安全又高效的并发治理方案。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询