文章目录

  • 一、什么是信号量
  • 二、信号量有什么用
  • 三、信号量怎么用
  • 四、有哪些核心方法
  • 五、参考博文


一、什么是信号量

Semaphore存在于java.util.concurrent(java并发)包下,,是一个计数信号量,维护了一个许可的集,若有必要,会在获取许可前阻塞每一个线程。

二、信号量有什么用

假想在服务器上运行着若干线程,这些线程需要连接到同一数据库,但任一时刻只能获得一定数目的数据库连接。要怎样才能够有效地将这些固定数目的数据库连接分配给若干线程?
方案一:给方法加锁,那样从始至终只会有一个数据库连接可用,此方案不可用;
方案二:使用Semaphore,通常用于限制并发访问某些资源(物理或逻辑的)的线程数目。此方案可行

三、信号量怎么用

构造函数

Semaphore(int permits,boolean fair) 
permits:许可数
fair:如果信号量想在征用时按照FIFO授予许可,则为true,否则为false,默认为false
Semaphore(int permits)

生活案例:排队上厕所,排队用餐,排队去银行柜台办理业务等等

@Test
    void testSemaphore() {
        // 建立缓存线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        // 定义信号量
        int permitsNum = 5;
        Semaphore semaphore = new Semaphore(permitsNum);
        // 启动20个线程
        for (int i = 0; i < 20; i++) {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    if(semaphore.availablePermits() > 0 ){
                        System.out.println("Thread " + Thread.currentThread().getName() + " 准备获取许可 " + "当前系统的并发数是:" + (permitsNum - semaphore.availablePermits()));
                    }else{
                        System.out.println("Thread " + Thread.currentThread().getName() + " 没有许可,排队等待 " + "当前系统的并发数是:" + (permitsNum - semaphore.availablePermits()));
                    }
                    try {
                        semaphore.acquire();
                        System.out.println("Thread " + Thread.currentThread().getName() + " 已经获取到许可 " + "当前系统的并发数是:" + (permitsNum - semaphore.availablePermits()));
                        Thread.sleep((long) (Math.random() * 10000));
                        System.out.println("Thread " + Thread.currentThread().getName() + " 任务执行完毕,准备归还许可");
                    } catch (Exception e) {
                        System.out.println("exception " + e.getMessage());
                    } finally {
                        semaphore.release();
                        System.out.println("Thread " + Thread.currentThread().getName() + " 已经归还许可,当前系统的并发数是:" + (permitsNum - semaphore.availablePermits()));
                    }
                }
            };
            // 执行线程
            executorService.execute(runnable);
        }
        // 关闭线程池
        executorService.shutdown();

        // Uninterruptibly 不间断地
        System.out.println("Thread " + Thread.currentThread().getName() + " 将线程阻塞");
        semaphore.acquireUninterruptibly(2);
        System.out.println("Thread " + Thread.currentThread().getName() + " 归还许可");
        semaphore.release(2);

    }

四、有哪些核心方法

1、void acquire()

Acquires a permit from this semaphore, blocking until one is available, or the thread is interrupted
从这个信号量获取许可证,阻塞直到有一个可用或者线程被中断

从信号量获取一个许可,在提供一个许可前当前线程会阻塞。如果成功获取了许可并立即返回,将信号量中的有用许可减一;如果获取失败,则抛出中断异常,线程被中断

2、void acquireUninterruptibly()

与acquire()方法相同,但是如果当前线程在等待许可时被中断,那么他会继续等待

3、boolean tryAcquire()

Acquires a permit from this semaphore, only if one is available at the time of invocation
如果只有一个信号量可用,则从该信号量获取许可证调用时间。

仅仅调用此信号量存在一个可用的许可,如果存在返回true ,不存在则返回false 并立即返回 。

4、boolean tryAcquire(0,TimeUnit.SECONDS)

在单位等待时间内获取许可,这样希望是遵守公平的设置,如果已经超出等待时间会立即返回。

5、void release()

释放一个许可,将其归还给指定的信号量