高级JAVA - 高并发下接口限流 Semaphore

Semaphore的介绍

Semaphore, 是JDK1.5的java.util.concurrent并发包中提供的一个并发工具类

Semaphore字面意思即信号量 , 个人认为比较容易理解的说法应该是 许可证管理器

官方的解释为

  • Semaphore是一个计数信号量
  • 从概念上将,Semaphore包含一组许可证
  • 如果有需要的话,每次调用acquire()方法都会阻塞,直到获取一个可用的许可证
  • 每次调用release()方法都会释放持有许可证的线程,并且归还Semaphore一个可用的许可证
  • 实际上并没有真实的许可证对象供线程使用, Semaphore只是对可用的数量进行管理维护

Semaphore内部主要通过AQS(AbstractQueuedSynchronizer)实现线程的管理 .

线程在运行时首先获取许可 , 如果成功 , 许可数就减1 , 线程运行 ; 当线程运行结束就释放许可 , 许可数就加1 ; 如果许可数为0 , 则获取失败 , 线程位于AQS的等待队列中 , 它会被其它释放许可的线程唤醒

Semaphore的构造器

Semaphore(int permits) : 初始化指定数量许可证的Semaphore
Semaphore(int permits, boolean fair) : 初始化指定数量许可证的Semaphore , 并指定是否公平模式(即FIFO先进先出原则)

Semaphore的常用的一些方法

boolean isFail() : 当前Semaphore是否是公平模式
int availablePermits() : 当前Semaphore可用的许可证数量
boolean hasQueuedThreads() : 判断当前Semaphore中是否存在正在等待许可证的线程
int getQueueLength() : 获取当前Semaphore中正在等待许可证的线程数量

void acquire() throws InterruptedException : 尝试获取一个可用的许可证 , 如果无可用许可证 , 当前线程会阻塞 , 如果线程被中断则抛出InterruptedException , 并停止阻塞继续执行
void acquire(int permits) throws InterruptedException : 尝试获取指定数量的可用许可证 , 如果无可用或数量不足 , 当前线程会阻塞 ,
如果线程被中断则抛出InterruptedException , 并停止阻塞继续执行
boolean tryAcquire() : 尝试获取1次可用许可证 , 非阻塞的 , 仅在调用该方法时尝试一次 , 获取到就继续执行并返回true . 获取不到也会停止等待继续执行 , 并返回false . 此外有重载方法tryAcquire(int permits)
boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException : 在限定时间内尝试获取1个可用许可证 , 如果获取到则继续执行返回true , 如果超时则继续执行并返回false,如果被中断则抛出一次异常并继续执行,此外有重载方法tryAcquire(int permits, long timeout, TimeUnit unit)
void acquireUninterruptibly() : 不可中断的尝试获取1个可用许可证 , 此外有重载方法acquireUninterruptibly(int permits)
int drainPermits() : 获取剩余所有可用的许可证

void release() : 释放1个可用的许可证 , 重载方法acquire(int permits)

Semaphore的代码演示

package com.xing.apiLimiting;

import lombok.AllArgsConstructor;
import org.apache.commons.lang3.RandomUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * @title 信号量Semaphore
 * @author Xingbz
 * @createDate 2018-12-7
 */
public class SemaphoreDemo {
    public static void main(String[] args) throws Exception {
//        demo1();//Semaphore的初始化 / 基本属性 / 获取释放操作
//        demo2();//多进程之间的获取/释放/等待
//        demo3();//实例场景
    }

    /**
     * @title Semaphore的初始化 / 基本属性 / 获取释放操作
     * @author Xingbz
     */
    private static void demo1() throws Exception {
        Semaphore semaphore1 = new Semaphore(5);//初始化指定数量许可证的Semaphore
        Semaphore semaphore2 = new Semaphore(5, true);//初始化指定数量许可证的Semaphore , 并指定为公平模式(FIFO)
        System.out.println("是否公平模式 : semaphore1 " + semaphore1.isFair() + "; semaphore2 " + semaphore2.isFair());//是否公平模式FIFO

        semaphore2.acquire();//尝试获取一个可用的许可证
        System.out.println("成功获取1个可用许可证 ; 剩余可用许可证数量 : " + semaphore2.availablePermits());

        semaphore2.release();//释放1个许可证
        System.out.println("成功释放1个许可证 ; 剩余可用许可证数量 : " + semaphore2.availablePermits());

        semaphore2.acquire(2);//尝试获取N个可用的许可证
        System.out.println("成功获取2个可用许可证 ; 剩余可用许可证数量 : " + semaphore2.availablePermits());

        semaphore2.release(2);//释放N个许可证 // 此时如果释放3个(>2) , 则可用数量亦会增加为6个?
        System.out.println("成功释放2个许可证 ; 剩余可用许可证数量 : " + semaphore2.availablePermits() + "\n");
    }

    /**
     * @title 多进程之间的获取/释放/等待
     * @author Xingbz
     */
    private static void demo2() throws Exception {
        Semaphore semaphore = new Semaphore(5, true);
        new Thread(() -> {//启动一个新线程获取所有可用许可证
            System.out.println(Thread.currentThread().getName() + "尝试获取剩余所有可用的许可证 . . .");
            int permits = semaphore.drainPermits();//获取剩余所有可用的许可证
            System.out.println(Thread.currentThread().getName() + "成功获取" + permits + "个可用许可证 ; 剩余可用许可证数量 : " + semaphore.availablePermits());
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            semaphore.release(permits);
            System.out.println(Thread.currentThread().getName() + "成功释放" + permits + "个许可证 ; 剩余可用许可证数量 : " + semaphore.availablePermits());
        }).start();
        Thread.sleep(100);
        System.out.println("\n[1]当前剩余可用的许可证数量 : " + semaphore.availablePermits());
        System.out.println("[1]否存在正在等待许可证的线程 : " + semaphore.hasQueuedThreads());
        System.out.println("[1]正在等待许可证的线程数量 : " + semaphore.getQueueLength());

        new Thread(() -> {//启动另外一个新线程尝试获取1个许可证
            try {
                System.out.println(Thread.currentThread().getName() + "尝试获取1个可用许可证 . . .");
                semaphore.acquire();//由于前一个线程获取了所有可用 , 所以这里会阻塞 , 直到前一个线程释放1个为止
                System.out.println(Thread.currentThread().getName() + "成功获取1个可用许可证 ; 剩余可用许可证数量 : " + semaphore.availablePermits());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            semaphore.release();
            System.out.println(Thread.currentThread().getName() + "成功释放1个许可证 ; 剩余可用许可证数量 : " + semaphore.availablePermits());

        }).start();
        Thread.sleep(100);
        System.out.println("\n[2]当前剩余可用的许可证数量 : " + semaphore.availablePermits());
        System.out.println("[2]否存在正在等待许可证的线程 : " + semaphore.hasQueuedThreads());
        System.out.println("[2]正在等待许可证的线程数量 : " + semaphore.getQueueLength());

        new Thread(() -> {//启动另外一个新线程尝试获取2个许可证
            try {
                System.out.println(Thread.currentThread().getName() + "尝试获取2个可用许可证 . . .");
                semaphore.acquire(2);
                System.out.println(Thread.currentThread().getName() + "成功获取2个可用许可证 ; 剩余可用许可证数量 : " + semaphore.availablePermits());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            semaphore.release(2);
            System.out.println(Thread.currentThread().getName() + "成功释放2个许可证 ; 剩余可用许可证数量 : " + semaphore.availablePermits());
        }).start();
        Thread.sleep(100);
        System.out.println("\n[3]当前剩余可用的许可证数量 : " + semaphore.availablePermits());
        System.out.println("[3]否存在正在等待许可证的线程 : " + semaphore.hasQueuedThreads());
        System.out.println("[3]正在等待许可证的线程数量 : " + semaphore.getQueueLength());

        Thread.sleep(6000);
        System.out.println("\n[终]当前剩余可用的许可证数量 : " + semaphore.availablePermits());
        System.out.println("[终]否存在正在等待许可证的线程 : " + semaphore.hasQueuedThreads());
        System.out.println("[终]正在等待许可证的线程数量 : " + semaphore.getQueueLength());
    }

    private static final ExecutorService executorService = Executors.newCachedThreadPool();

    /**
     * @title 实例场景
     * @description
     *  场景说明 :
     *  1 . 模拟出口车辆许可的场景
     *  2 . 路段有2个车辆出口
     *          初始化一个permits=2的Semaphore对象
     *  3 . 某时段内共有20个车辆依次出行
     *          该Semaphore对象遵循公平(FIFO)原则 , 定义20个请求线程
     *  4 . 每个车辆出去的速度不同 , 耗费时间也不同
     *  5 . 有的车辆会一直等待到出口放行
     *          调用acquireUninterruptibly()方法 , 不会中断
     *  6 . 有的车辆没有耐心 , 等待时间超过自己预期 , 则不再排队 , 折返回去
     *          调用tryAcquire(timeout,TimeUnit)方法 , 且等待超时
     *  7 . 有的车辆很有耐心 , 但突然接到电话 , 不再排队 , 去其他地点了
     *          调用了acquire()方法 , 且被中断
     * @author Xingbz
     */
    private static void demo3() throws Exception {
        Semaphore semaphore = new Semaphore(2, true);
        List futureList = new ArrayList<>(5);//某车队的车辆 , 排队中途会被电话中断
        for (int i = 1; i <= 20; i++) {
            if (i <= 10) {//前10辆车耐心排队等候许可
                executorService.execute(new Car("耐心车辆" + i, semaphore, 1));
            } else if (i <= 15) {//这5个车辆没有耐心  , 只愿等待少许
                executorService.execute(new Car("折返车辆" + i, semaphore, 2));
            } else {//这5个车辆被中断
                Future future = executorService.submit(new Thread(new Car("中断车辆" + i, semaphore, 3)));
                futureList.add(future);
            }
        }
        Thread.sleep(10000);//整体排队10S后 , 某车队被电话集体召回
        System.out.println("车队众人接到电话 , 集体终止排队");
        futureList.forEach(f -> f.cancel(true));
    }

    @AllArgsConstructor
    static class Car implements Runnable {
        /** 车辆编号 */
        private String carNo;
        /** 许可证 */
        private Semaphore semaphore;
        /** 行动方式 : 1一直等待直到许可 ; 2没有耐心折返回去 ; 3等待中被中断 */
        private int type;

        /**
         * @title 许可
         * @author Xingbz
         */
        @Override
        public void run() {
            switch (type) {
                case 1: //该车辆很有耐心  ,一直等到许可
                    semaphore.acquireUninterruptibly();
                    try {
                        Thread.sleep(RandomUtils.nextLong(3000, 4000));//正在许可 . . .
                    } catch (InterruptedException e) {
                    }
                    semaphore.release();//许可完毕 , 将许可证归还
                    System.out.println(carNo + "已成功许可 . ");
                    break;

                case 2://该车辆没有耐心 , 等待1s没有获取许可证 , 就折返了
                    try {
                        if (semaphore.tryAcquire(RandomUtils.nextInt(6000, 11000), TimeUnit.MILLISECONDS)) {
                            Thread.sleep(RandomUtils.nextLong(3000, 4000));//正在许可 . . .
                            semaphore.release();//许可完毕 , 将许可证归还
                            System.out.println(carNo + "已成功许可 . ");
                        } else {//超时则不再等待
                            System.out.println(carNo + "折返 . ");
                        }
                    } catch (InterruptedException e) {
                    }
                    break;

                case 3://该车辆等待中被中断
                    try {
                        semaphore.acquire();
                        Thread.sleep(RandomUtils.nextLong(3000, 4000));//正在许可 . . .
                        semaphore.release();
                        System.out.println(carNo + "已成功许可 . ");
                    } catch (InterruptedException e) {//被电话 中断  , 不再等待
                        System.out.println(carNo + "中断 , 驶向他处 . ");
                    }
            }
        }
    }
}

 

你可能感兴趣的:(Java,高级JAVA)