CAS 令牌桶

并发控制

       在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流

  • 缓存:缓存的目的是提升系统访问速度和增大系统处理容量
  • 降级:降级是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行
  • 限流:限流的目的是通过对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理

CAS

  1. 什么是CAS
    CAS: 全称Compare and swap,字面意思:”比较并交换“,一个 CAS 涉及到以下操作:我们假设内存中的原数据V,旧的预期值A,需要修改的新值B。1. 比较 A 与 V 是否相等。(比较)2. 如果比较相等,将 B 写入 V。(交换) 3. 返回操作是否成功。当多个线程同时对某个资源进行CAS操作,只能有一个线程操作成功,但是并不会阻塞其他线程,其他线程只会收到操作失败的信号。可见 CAS 其实是一个乐观锁。
  2. CAS的应用
  • 自旋锁,获取锁的可以执行,没有获取锁的需要一直自旋
public class SpinLock {

  private AtomicReference<Thread> sign =new AtomicReference<>();

  public void lock(){
    Thread current = Thread.currentThread();
    while(!sign .compareAndSet(null, current)){
    }
  }

  public void unlock (){
    Thread current = Thread.currentThread();
    sign .compareAndSet(current, null);
  }
}
  • java.util.concurrent.atomic 包下的类基本都是利用CAS 实现的,通过源码可以发现基本都是调用unsafe 类,然后Unsafe 类调用本地的native方法compareAndSwap… 来实现CAS操作,atomic包下的类并不是所有的方法都采用了CAS+自旋,要通过具体情况进行分析。一般在源码中使用了while 的基本都是加了自旋,否则不带自旋
  • 令牌桶限流

限流

        经常在调别人的接口的时候会发现有限制,比如微信公众平台接口、聚合API等等这样的,对方会限制每天最多调多少次或者每分钟最多调多少次,这就是限流的具体体现
在这里插入图片描述

令牌桶算法

  1. 以下直接贴出令牌桶算法代码(仿照Eurake)
public class RateLimiter {
    private final AtomicInteger load = new AtomicInteger();
    private final AtomicLong lastFrushTime = new AtomicLong(0L);

    private final int rateType;

    /**
     * 初始化构造函数
     *
     * @param dateType 时间类型
     * @throws Exception
     */
    public RateLimiter(TimeUnit dateType) {
        switch (dateType) {
            case SECONDS:
                this.rateType = 1000;
                break;
            case MINUTES:
                this.rateType = 60 * 1000;
                break;
            default:
                throw new IllegalArgumentException("不支持的时间参数类型");
        }
        this.lastFrushTime.set(System.currentTimeMillis());
    }

    /**
     * 请求凭证
     *
     * @param bucketSize     凭证桶大小
     * @param credentialRate 凭证产生速率
     * @return true:成功,false:失败
     */
    public boolean getCredential(int bucketSize, int credentialRate) {
        if (bucketSize <= 0 || credentialRate <= 0) {
            return true;        //  如果桶大小或者凭证产生速率有任意一个小于等于0,则认为不需要限流
        }
        //  刷新凭证桶
        refillCredential(credentialRate);
        //  消费凭证
        return consumCredential(bucketSize);
    }

    /**
     * 刷新凭证桶
     *
     * @param credentialRate 凭证产生速率
     */
    public void refillCredential(int credentialRate) {
        //  获取最后一次刷新桶时间
        long lastTime = this.lastFrushTime.get();
        //  获取系统当前时间
        long currentTimeMillis = System.currentTimeMillis();
        //  根据时间差值计算出 时间差值内所需产生的令牌
        long diffTime = currentTimeMillis - lastTime;
        long needCredential = diffTime * credentialRate / rateType;
        //  所需新产生令牌大于0 则继续往下执行
        if (needCredential > 0) {
            //  重新设置成功才会填充凭证
            if (lastFrushTime.compareAndSet(lastTime, currentTimeMillis)) {
                //  获取被消费的凭证数
                int consumed = load.get();
                //  计算还剩多少被消费的,最小为0
                int min = (int) Math.max(0, consumed - needCredential);
                //  将计算的剩余被消费的设置进去
                load.compareAndSet(consumed, min);
            }
        }
    }

    /**
     * 消费凭证桶
     *
     * @param bucketSize 凭证桶大小
     * @return
     */
    public boolean consumCredential(int bucketSize) {
        while (true) {
            int consumed = load.get();
            //  判断凭证消费数量是否达到上限
            if (consumed >= bucketSize) {
                return false;
            }
            if (load.compareAndSet(consumed, consumed + 1)) {
                return true;
            }
        }
    }

    /**
     * 重置
     */
    public void reset() {
        this.load.set(0);
        this.lastFrushTime.set(System.currentTimeMillis());
    }
}

总结

加油噢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值