Java 限流算法之令牌桶实现

文章目录

计数法实现

采用计数法实现,通过子任务定期向桶中添加令牌。

package scenequestion;

import java.util.concurrent.*;

/**
 * 令牌桶 计数法实现
 *
 * @author wenei
 * @date 2021-08-15 10:15
 */
public class TokenBucket {

    private static final String TOKEN = "TOKEN";

    /**
     * 保证其内部方法都安全
     */
    private ArrayBlockingQueue<String> blockingQueue;

    private int limit;

    private int period;

    private ScheduledExecutorService subThreadService;

    public TokenBucket(int limit, int period) {
        this.limit = limit;
        this.period = period;
        blockingQueue = new ArrayBlockingQueue<>(limit);
        for (int i = 0; i < limit; i++) {
            blockingQueue.add(TOKEN);
        }
        subThreadService = Executors.newScheduledThreadPool(1);
        // 定期执行添加令牌任务
        subThreadService.scheduleAtFixedRate(this::addToken, 10, period, TimeUnit.MILLISECONDS);
    }

    public boolean tryAcquire() {
        return blockingQueue.poll() != null;
    }

    private void addToken() {
        blockingQueue.add(TOKEN);
    }

    private void close() {
        subThreadService.shutdownNow();
    }

    public static void main(String[] args) throws InterruptedException {
        int threadCount = 10;
        TokenBucket limiter = new TokenBucket(5, 1000);
        ExecutorService threadPool = new ThreadPoolExecutor(
        	0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>()
        );
        // 使用锁控制10个线程完成后再处理
        CountDownLatch latch = new CountDownLatch(threadCount);
        for (int i = 0; i < threadCount; i++) {
            int finalI = i;
            // 等待4s让令牌加满
            if (finalI == 7) {
                Thread.sleep(4000);
            }
            threadPool.execute(() -> {
                while (true) {
                    if (limiter.tryAcquire()) {
                        System.out.println("线程" + finalI + "获取成功");
                        latch.countDown();
                        break;
                    } else {
                        continue;
                    }
                }
            });
        }
        latch.await();
        threadPool.shutdown();
        limiter.close();
    }
}
令牌桶算法是一种常见的限流算法,它可以控制请求的速率,防止系统被过多的请求压垮。下面是Java实现令牌桶算法的步骤和代码逻辑: 1. 定义一个令牌桶类,包含以下属性: - 最后一次令牌发放时间 - 桶的容量 - 令牌生成速度 - 当前令牌数量 2. 实现一个获取令牌的方法,该方法会在每次请求到来时被调用,具体实现如下: - 计算当前令牌数量 - 判断当前令牌数量是否足够 - 如果令牌数量不足,则拒绝请求 - 如果令牌数量足够,则领取令牌,并执行业务逻辑 3. 使用ScheduledExecutorService定时生成令牌,具体实现如下: - 每隔一段时间生成一定数量的令牌 - 如果令牌数量超过桶的容量,则不再生成令牌 下面是Java实现令牌桶算法的代码逻辑: ``` @Slf4j public class TokensLimiter { private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5); // 最后一次令牌发放时间 public long timeStamp = System.currentTimeMillis(); // 桶的容量 public int capacity = 10; // 令牌生成速度10/s public int rate = 10; // 当前令牌数量 public int tokens; public void acquire() { scheduledExecutorService.scheduleWithFixedDelay(() -> { long now = System.currentTimeMillis(); // 当前令牌数 tokens = Math.min(capacity, (int) (tokens + (now - timeStamp) * rate / 1000)); // 每隔0.5秒发送随机数量的请求 int permits = (int) (Math.random() * 9) + 1; log.info("请求令牌数:" + permits + ",当前令牌数:" + tokens); timeStamp = now; if (tokens < permits) { // 若不到令牌,拒绝 log.info("限流了"); } else { // 还有令牌,领取令牌 tokens -= permits; log.info("剩余令牌=" + tokens); } }, 1000, 500, TimeUnit.MILLISECONDS); } public static void main(String[] args) { TokensLimiter tokensLimiter = new TokensLimiter(); tokensLimiter.acquire(); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肥牛火锅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值