java实现令牌桶算法

此实现是根据GUAVA实现的令牌桶算法限流的思路,在每次尝试获取token时进行计算当前可获得的token数量,相当于懒汉式生成令牌,无需额外的线程去生成令牌。况且,额外线程去生成令牌,需要消耗线程资源,在并发量较高时,定时任务并不准确,不能准确生成令牌。

package concurrencylimit;

import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 令牌桶算法
 *
 * @author nekle
 * @since 2022/8/9
 */
public class TokenBucket {
	// 上一次获取令牌的时间
    private AtomicLong beforeAtomic;
    // 生成令牌的时间间隔
    private int interval;
    // 最大令牌数量
    private int max;
    // 用于控制令牌数量修改的可重入锁
    private ReentrantLock lock;
    public TokenBucket(int interval, int max) {
        this.beforeAtomic = new AtomicLong(System.currentTimeMillis() - interval * max);
        this.interval = interval;
        this.max = max;
        this.lock = new ReentrantLock(true);
    }

    public boolean acquire() {
        boolean flag = false;
        // 当前线程自旋尝试获取令牌
        while (!flag) {
        	// 当前获取令牌时间
            long now = System.currentTimeMillis();
            // 上一次获取令牌时间
            long before = this.beforeAtomic.get();
            // 计算时间间隔内生成的令牌数
            long tokenSum = ((now - before) / interval);
            // 有剩余令牌,则尝试获取令牌
            if (tokenSum > 0) {
                boolean locked = false;
                // 如果令牌数量超过设置的上限,则将令牌数量归于上限值
                if (tokenSum > max) {
                	// 可重入锁对令牌数修改操作进并发访问控制
                    locked = lock.tryLock();
                    if (locked) {
                    	// 成功拿到锁,则进行修改,将令牌数量归于上限值
                        this.beforeAtomic.set(now - interval * max + interval);
                        flag = true;
                        lock.unlock();
                    }
                }
                // 若尝试获得锁失败,说明另一个线程正在修改最后一次获得令牌的时间
                if (!locked) {
                	// CAS尝试修改最后一次获取令牌的时间,成功则完成这次获取令牌操作,否则继续自旋尝试获得令牌
                    flag = this.beforeAtomic.compareAndSet(before, before + interval);
                }
            } else {
            	// 无剩余令牌,尝试获取令牌失败
                return false;
            }
        }
        return true;
    }
}

最近在学习令牌桶算法,自己实现了一个,欢迎各位交流学习!!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
令牌桶算法是一种限流算法,它可以控制某个服务或接口的并发访问数量,防止系统被过多的请求占用,导致系统负载过高。下面是Java实现令牌桶算法的示例代码: ``` public class TokenBucket { private long capacity; // 桶的容量 private long rate; // 令牌放置速度 private long tokens; // 当前令牌数量 private long lastRefillTime; // 上次放置令牌的时间 public TokenBucket(long capacity, long rate) { this.capacity = capacity; this.rate = rate; this.tokens = capacity; this.lastRefillTime = System.currentTimeMillis(); } /** * 尝试获取令牌 * @return 是否获取到令牌 */ public synchronized boolean tryAcquire() { refill(); if (tokens > 0) { tokens--; return true; } return false; } /** * 放置令牌 */ private void refill() { long now = System.currentTimeMillis(); if (tokens < capacity) { long tokensToAdd = (now - lastRefillTime) * rate / 1000; tokens = Math.min(tokens + tokensToAdd, capacity); } lastRefillTime = now; } } ``` 在这个示例中,令牌桶算法的核心在于 `tryAcquire()` 方法,它用于尝试获取令牌。在这个方法中,我们首先调用 `refill()` 方法,用于放置令牌。然后判断桶中是否有令牌,如果有,就将令牌数量减一,并返回获取到令牌;否则返回获取不到令牌。 放置令牌的方法 `refill()` 会计算两次放置令牌之间的时间差,根据时间差和令牌的放置速度计算出应该放置的令牌数量。如果桶中令牌数量小于容量,就将放置的令牌数量和当前令牌数量相加,但不超过桶的容量。这样就可以保证令牌桶中的令牌数量不会超过容量。 使用令牌桶算法可以有效地限制系统的并发访问数量,避免系统负载过高导致服务不稳定。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值