java Semaphore 信号量理解与示例

是什么

给定一定数量的许可证,用于调用者获取,如果许可证都被占用,那么就进入阻塞状态;
可以理解为上公共厕所,厕所只有2个坑位,目前有3个人,那么同时只能有2个人进去,第三人想要进去那么就必须等占用坑位的人完事之后才OK
目前在业务上没遇到信号量场景,能想到的就是对单体应用请求限流,下面就给出一个示例

使用信号量限流示例

import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 *
 * 信号量实现限流示例
 * @author 唐海斌
 * @date 2022/8/17 13:24
 */
public class SemaphoreLimit {
    /**
     * 同时允许2个客户端同时访问资源
     */
    static Semaphore semaphore = new Semaphore(2);

    public static void limit()  {
        try {
            //当未获取到许可会阻塞,直到有新的许可
            semaphore.acquire();
            System.out.println(LocalDateTime.now() + "------>>>> 获取到许可...");
            //将线程阻塞10秒,模拟业务处理
            LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(10));
            semaphore.release();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int index = 0; index < 10; ++index) {
            executorService.execute(SemaphoreLimit::limit);
        }
    }
}

原理

从源码上分析

 abstract static class Sync extends AbstractQueuedSynchronizer {
    //非公平信号量实现
    final int nonfairTryAcquireShared(int acquires) {
        for (; ; ) {
            //获取可用许可数量
            int available = getState();
            //剩余许可数 = 可用许可数 - 请求许可数
            int remaining = available - acquires;
            //可用许可数小于0直接返回,在调用方死循环调用当前方法;可用许可数大于0就采用CAS比较更新剩余许可数的值
            if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                return remaining;
        }
    }
}

总结

内部实现了AQS同步器,采用许可数递减的方式发放许可,当许可数小于1就死循环获取许可直到有空闲的许可证;所以能限制在同一时间内最多有指定数量的线程可以访问资源

posted @ 2022-08-17 14:12  四码难追  阅读(86)  评论(0)    收藏  举报