手撸JDK之Semaphore那点事

前置思想

首先明确一点,削峰限流不一定非要用MQ队列,其实JDK就给我们提供了良好的环境Semaphore他主要作用是以【时间换空间】也就是我们现在处理不了的请求让他稍等一会,之后我们闲暇的空间了在处理他,此处主要是JDK已经实现了削峰限流所以手撸一个让自己更加深刻。

主要思想

Semaphore无非就是将不能处理的请求放入队列中,在可以处理的时候拿出。

主要使用到的类

  • LinkedBlockingQueue:此处当然少不了队列,当然此队列还有一个作用,谁哪的令牌谁放回去
  • AtomicInteger:主要判断什么时候进行阻塞
  • LockSupport:线程状态的切换

交代完了开怼!!

/**
     * 用于存储线程
     */
    private LinkedBlockingQueue<Thread> requestQueue = new LinkedBlockingQueue<>();
    /**
     * 外部传入令牌数量,用于什么时候让线程进入阻塞
     */
    private AtomicInteger tokenNumber;

    public MySemaphore(Integer tokenNumber) {
    
    
        this.tokenNumber = new AtomicInteger(tokenNumber);
    }

这步没啥可说的继续!!

/**
     * 是否能获取令牌
     */
    public boolean tryAcquire() {
    
    
        if (!requestQueue.contains(Thread.currentThread())) {
    
    
            requestQueue.add(Thread.currentThread());
        }
        return tokenNumber.get() > 0;
    }

主要判断是否可以获取令牌,以及不将重复的线程放入队列中

/**
     * 获取令牌
     */
    public void acquire() {
    
    
        //当前令牌捅中还有令牌,则进行自减,没有则阻塞进入等待队列
        if ( tryAcquire()) {
    
    
            tokenNumber.decrementAndGet();
        } else {
    
    
            LockSupport.park();
        }
    }

获取令牌则将筒中的令牌-1,没有则阻塞

/**
     * 用完了释放令牌 只有是当前队列中的线程才允许释放令牌,并且检测是否在休眠线程,是则唤醒
     */
    public void release() {
    
    
        if (requestQueue.remove(Thread.currentThread())) {
    
    
            tokenNumber.incrementAndGet();
            for (Thread thread : requestQueue) {
    
    
                if (!thread.getState().equals(Thread.State.RUNNABLE)) {
    
    
                    LockSupport.unpark(requestQueue.peek());
                }
            }
        }
    }

当然是有借有还再借不难啦,此处主要考虑的是是否是当前队列中的线程还回令牌,以及线程的状态,主要是以唤醒了别再二次唤醒了,当然这地方只考虑到唤醒了就不唤醒了,还没考虑线程其他状态
PS:此处还差公平,但是一直实现不了,主要还剩一个死活不被唤醒很蛋疼!如有大手看到请不吝啬赐教!!!

猜你喜欢

转载自blog.csdn.net/cj181jie/article/details/108734434