阅读 204

令牌桶限流-Java简易实现

关于限流有多种方式,项目中经常使用Guava RateLimiter,简单又方便。令牌桶的原理不多说,有大把的资料。

许多大厂的笔试题中,会高频出现限流算法题。文中提了几种简易方式,算是做个记录吧。话不多说,直接贴代码。代码中只贴了令牌桶的方式,直接计数信号量也差不多,就没贴了。

/**
 * User: yclxiao
 * Desc: 限流算法
 *
 * 1、令牌桶限流算法
 * 核心概念:
 *  有个任务每秒往桶里放N个token,不能超过最大限制。。。
 *  每次进来执行任务,检查token是否>=1,>=1则token--再执行任务,
 *  如果token < 1,则直接拒绝
 *  优点:可以瞬间承载大流量
 *
 * 2、其余方法:
 *
 *  1、直接计数法
 *     atomicInteger 直接计数,同时有个固定速率的线程每秒把atomicInteger清零
 *
 *  2、信号量法
 *     SemaphoreOne信号量,获取到信号量则开始执行任务,获取失败,则拒绝。
 *     最后释放release。。。同时有个固定速率的线程每秒把信号量全部释放
 */
public class TokenBucket {

    /**
     * 定义了桶
     */
    public class Bucket {
        //容量
        int capacity;
        //速率,每秒放多少
        int rateCount;
        //目前token个数
        volatile AtomicInteger curCount = new AtomicInteger(0);

        public Bucket(int capacity, int rateCount) {
            this.capacity = capacity;
            this.rateCount = rateCount;
        }

        public void put() {
            System.out.println(Thread.currentThread().getName() + "====== put之前的数量:" + curCount.get());
            if (curCount.get() < capacity) {
                System.out.println("目前数量==" + curCount.get() + ", 我还可以继续放");
                curCount.addAndGet(rateCount);
            }
        }

        public boolean get() {
            System.out.println(Thread.currentThread().getName() + "====== get之前的数量:" + curCount.get());
            if (curCount.get() >= 1) {
                curCount.decrementAndGet();
                return true;
            }
            return false;
        }
    }

    @Test
    public void testTokenBucket() throws InterruptedException {

        Bucket bucket = new Bucket(5, 2);

        //固定线程,固定的速率往桶里放数据,比如每秒N个
        ScheduledThreadPoolExecutor scheduledCheck = new ScheduledThreadPoolExecutor(1);
        scheduledCheck.scheduleAtFixedRate(() -> {
            bucket.put();
        }, 0, 1, TimeUnit.SECONDS);

        //先等待一会儿,让桶里放点token
        Thread.sleep(6000);

        //模拟瞬间10个线程进来拿token
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                if (bucket.get()) {
                    System.out.println(Thread.currentThread() + "获取到了资源");
                } else {
                    System.out.println(Thread.currentThread() + "被拒绝");
                }
            }).start();
        }

        //等待,往桶里放token
        Thread.sleep(3000);

        //继续瞬间10个线程进来拿token
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                if (bucket.get()) {
                    System.out.println(Thread.currentThread() + "获取到了资源");
                } else {
                    System.out.println(Thread.currentThread() + "被拒绝");
                }
            }).start();
        }
    }
}
复制代码
关注下面的标签,发现更多相似文章
评论