Java充电社
专辑
博文
联系我
本人继续续收门徒,亲手指导
Java高并发教程
-> 实战:一起来搞懂限流
1、必须知道的几个概念
2、并发级别
3、有关并行的两个重要定律
4、JMM相关的一些概念
5、深入理解进程和线程
6、线程的基本操作
7、volatile与Java内存模型
8、线程组
9、用户线程和守护线程
10、线程安全和synchronized
11、中断线程的几种方式
12、JUC中ReentrantLock
13、JUC中的Condition
14、JUC中的LockSupport工具类
15、UC中的Semaphore(信号量)
16、JUC中的CountDownLatch
17、JUC中的循环栅栏CyclicBarrier
18、线程池
19、JUC中的Executor框架详解1
20、JUC中的Executor框架详解2
21、java中的CAS
22、java中的UnSafe类
23、JUC中的原子操作类
24、ThreadLocal、InheritableThreadLocal
25、JUC中的阻塞队列
26、JUC中一些常见的集合
27、实战:你的接口太慢了需要优化
28、实战:构建日志系统
29、实战:一起来搞懂限流
30、JUC中的CompletableFuture
31、等待线程完成的方式你知道几种?
32、原子操作增强类LongAdder、LongAccumulator
33、怎么演示公平锁和非公平锁
34、谷歌提供的一些好用的并发工具类
35、延迟队列 DelayQueue 详解
36、线程6种状态详解
37、如何实现一个通用的延迟队列?
上一篇:实战:构建日志系统
下一篇:JUC中的CompletableFuture
<div style="display:none"></div> ## 本文内容 1. 介绍常见的限流算法 2. 通过控制最大并发数来进行限流 3. 通过漏桶算法来进行限流 4. 通过令牌桶算法来进行限流 5. 限流工具类RateLimiter ## 常见的限流的场景 1. 秒杀活动,数量有限,访问量巨大,为了防止系统宕机,需要做限流处理 2. 国庆期间,一般的旅游景点人口太多,采用排队方式做限流处理 3. 医院看病通过发放排队号的方式来做限流处理。 ## 常见的限流算法 1. 通过控制最大并发数来进行限流 2. 使用漏桶算法来进行限流 3. 使用令牌桶算法来进行限流 ## 通过控制最大并发数来进行限流 以秒杀业务为例,10个iphone,100万人抢购,100万人同时发起请求,最终能够抢到的人也就是前面几个人,后面的基本上都没有希望了,那么我们可以通过控制并发数来实现,比如并发数控制在10个,其他超过并发数的请求全部拒绝,提示:秒杀失败,请稍后重试。 并发控制的,通俗解释:一大波人去商场购物,必须经过一个门口,门口有个门卫,兜里面有指定数量的门禁卡,来的人先去门卫那边拿取门禁卡,拿到卡的人才可以刷卡进入商场,拿不到的可以继续等待。进去的人出来之后会把卡归还给门卫,门卫可以把归还来的卡继续发放给其他排队的顾客使用。 JUC中提供了这样的工具类:Semaphore,示例代码: ```java package com.itsoku.chat29; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; /** * 跟着阿里p7学并发,微信公众号:javacode2018 */ public class Demo1 { static Semaphore semaphore = new Semaphore(5); public static void main(String[] args) { for (int i = 0; i < 20; i++) { new Thread(() -> { boolean flag = false; try { flag = semaphore.tryAcquire(100, TimeUnit.MICROSECONDS); if (flag) { //休眠2秒,模拟下单操作 System.out.println(Thread.currentThread() + ",尝试下单中。。。。。"); TimeUnit.SECONDS.sleep(2); } else { System.out.println(Thread.currentThread() + ",秒杀失败,请稍微重试!"); } } catch (InterruptedException e) { e.printStackTrace(); } finally { if (flag) { semaphore.release(); } } }).start(); } } } ``` 输出: ```java Thread[Thread-10,5,main],尝试下单中。。。。。 Thread[Thread-8,5,main],尝试下单中。。。。。 Thread[Thread-9,5,main],尝试下单中。。。。。 Thread[Thread-12,5,main],尝试下单中。。。。。 Thread[Thread-11,5,main],尝试下单中。。。。。 Thread[Thread-2,5,main],秒杀失败,请稍微重试! Thread[Thread-1,5,main],秒杀失败,请稍微重试! Thread[Thread-18,5,main],秒杀失败,请稍微重试! Thread[Thread-16,5,main],秒杀失败,请稍微重试! Thread[Thread-0,5,main],秒杀失败,请稍微重试! Thread[Thread-3,5,main],秒杀失败,请稍微重试! Thread[Thread-14,5,main],秒杀失败,请稍微重试! Thread[Thread-6,5,main],秒杀失败,请稍微重试! Thread[Thread-13,5,main],秒杀失败,请稍微重试! Thread[Thread-17,5,main],秒杀失败,请稍微重试! Thread[Thread-7,5,main],秒杀失败,请稍微重试! Thread[Thread-19,5,main],秒杀失败,请稍微重试! Thread[Thread-15,5,main],秒杀失败,请稍微重试! Thread[Thread-4,5,main],秒杀失败,请稍微重试! Thread[Thread-5,5,main],秒杀失败,请稍微重试! ``` 关于`Semaphore`的使用,可以移步:[JUC中的Semaphore(信号量)](/course/1/15) ## 使用漏桶算法来进行限流 国庆期间比较火爆的景点,人流量巨大,一般入口处会有限流的弯道,让游客进去进行排队,排在前面的人,每隔一段时间会放一拨进入景区。排队人数超过了指定的限制,后面再来的人会被告知今天已经游客量已经达到峰值,会被拒绝排队,让其明天或者以后再来,这种玩法采用漏桶限流的方式。 漏桶算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率。 漏桶算法示意图: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/29/d3e56348-8066-4fa2-a71e-557f518cfe35.png) 简陋版的实现,代码如下: ```java package com.itsoku.chat29; import java.util.Objects; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.LockSupport; /** * 跟着阿里p7学并发,微信公众号:javacode2018 */ public class Demo2 { public static class BucketLimit { static AtomicInteger threadNum = new AtomicInteger(1); //容量 private int capcity; //流速 private int flowRate; //流速时间单位 private TimeUnit flowRateUnit; private BlockingQueue<Node> queue; //漏桶流出的任务时间间隔(纳秒) private long flowRateNanosTime; public BucketLimit(int capcity, int flowRate, TimeUnit flowRateUnit) { this.capcity = capcity; this.flowRate = flowRate; this.flowRateUnit = flowRateUnit; this.bucketThreadWork(); } //漏桶线程 public void bucketThreadWork() { this.queue = new ArrayBlockingQueue<Node>(capcity); //漏桶流出的任务时间间隔(纳秒) this.flowRateNanosTime = flowRateUnit.toNanos(1) / flowRate; Thread thread = new Thread(this::bucketWork); thread.setName("漏桶线程-" + threadNum.getAndIncrement()); thread.start(); } //漏桶线程开始工作 public void bucketWork() { while (true) { Node node = this.queue.poll(); if (Objects.nonNull(node)) { //唤醒任务线程 LockSupport.unpark(node.thread); } //休眠flowRateNanosTime LockSupport.parkNanos(this.flowRateNanosTime); } } //返回一个漏桶 public static BucketLimit build(int capcity, int flowRate, TimeUnit flowRateUnit) { if (capcity < 0 || flowRate < 0) { throw new IllegalArgumentException("capcity、flowRate必须大于0!"); } return new BucketLimit(capcity, flowRate, flowRateUnit); } //当前线程加入漏桶,返回false,表示漏桶已满;true:表示被漏桶限流成功,可以继续处理任务 public boolean acquire() { Thread thread = Thread.currentThread(); Node node = new Node(thread); if (this.queue.offer(node)) { LockSupport.park(); return true; } return false; } //漏桶中存放的元素 class Node { private Thread thread; public Node(Thread thread) { this.thread = thread; } } } public static void main(String[] args) { BucketLimit bucketLimit = BucketLimit.build(10, 60, TimeUnit.MINUTES); for (int i = 0; i < 15; i++) { new Thread(() -> { boolean acquire = bucketLimit.acquire(); System.out.println(System.currentTimeMillis() + " " + acquire); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } } } ``` 代码中`BucketLimit.build(10, 60, TimeUnit.MINUTES);`创建了一个容量为10,流水为60/分钟的漏桶。 代码中用到的技术有: 1. [BlockingQueue阻塞队列](/course/1/25) 2. [JUC中的LockSupport工具类,必备技能](/course/1/14) ## 使用令牌桶算法来进行限流 令牌桶算法的原理是系统以恒定的速率产生令牌,然后把令牌放到令牌桶中,令牌桶有一个容量,当令牌桶满了的时候,再向其中放令牌,那么多余的令牌会被丢弃;当想要处理一个请求的时候,需要从令牌桶中取出一个令牌,如果此时令牌桶中没有令牌,那么则拒绝该请求。从原理上看,令牌桶算法和漏桶算法是相反的,一个“进水”,一个是“漏水”。这种算法可以应对突发程度的请求,因此比漏桶算法好。 令牌桶算法示意图: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/29/aa451e3d-8f6d-4913-b573-2de04377ff13.png) 有兴趣的可以自己去实现一个。 ## 限流工具类RateLimiter Google开源工具包Guava提供了限流工具类RateLimiter,可以非常方便的控制系统每秒吞吐量,示例代码如下: ```java package com.itsoku.chat29; import com.google.common.util.concurrent.RateLimiter; import java.util.Calendar; import java.util.Date; import java.util.Objects; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.LockSupport; /** * 跟着阿里p7学并发,微信公众号:javacode2018 */ public class Demo3 { public static void main(String[] args) throws InterruptedException { RateLimiter rateLimiter = RateLimiter.create(5);//设置QPS为5 for (int i = 0; i < 10; i++) { rateLimiter.acquire(); System.out.println(System.currentTimeMillis()); } System.out.println("----------"); //可以随时调整速率,我们将qps调整为10 rateLimiter.setRate(10); for (int i = 0; i < 10; i++) { rateLimiter.acquire(); System.out.println(System.currentTimeMillis()); } } } ``` 输出: ```java 1566284028725 1566284028922 1566284029121 1566284029322 1566284029522 1566284029721 1566284029921 1566284030122 1566284030322 1566284030522 ---------- 1566284030722 1566284030822 1566284030921 1566284031022 1566284031121 1566284031221 1566284031321 1566284031422 1566284031522 1566284031622 ``` 代码中`RateLimiter.create(5)`创建QPS为5的限流对象,后面又调用`rateLimiter.setRate(10);`将速率设为10,输出中分2段,第一段每次输出相隔200毫秒,第二段每次输出相隔100毫秒,可以非常精准的控制系统的QPS。 上面介绍的这些,业务中可能会用到,也可以用来应对面试。 <a style="display:none" target="_blank" href="https://mp.weixin.qq.com/s/_S1DD2JADnXvpexxaBwLLg" style="color:red; font-size:20px; font-weight:bold">继续收门徒,亲手带,月薪 4W 以下的可以来找我</a> ## 最新资料 1. <a href="https://mp.weixin.qq.com/s?__biz=MzkzOTI3Nzc0Mg==&mid=2247484964&idx=2&sn=c81bce2f26015ee0f9632ddc6c67df03&scene=21#wechat_redirect" target="_blank">尚硅谷 Java 学科全套教程(总 207.77GB)</a> 2. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484192&idx=1&sn=505f2faaa4cc911f553850667749bcbb&scene=21#wechat_redirect" target="_blank">2021 最新版 Java 微服务学习线路图 + 视频</a> 3. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484573&idx=1&sn=7f3d83892186c16c57bc0b99f03f1ffd&scene=21#wechat_redirect" target="_blank">阿里技术大佬整理的《Spring 学习笔记.pdf》</a> 4. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484544&idx=2&sn=c1dfe907cfaa5b9ae8e66fc247ccbe84&scene=21#wechat_redirect" target="_blank">阿里大佬的《MySQL 学习笔记高清.pdf》</a> 5. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485167&idx=1&sn=48d75c8e93e748235a3547f34921dfb7&scene=21#wechat_redirect" target="_blank">2021 版 java 高并发常见面试题汇总.pdf</a> 6. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485664&idx=1&sn=435f9f515a8f881642820d7790ad20ce&scene=21#wechat_redirect" target="_blank">Idea 快捷键大全.pdf</a> ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/1/2883e86e-3eff-404a-8943-0066e5e2b454.png)
#custom-toc-container