JUC:04.Semaphore信号量

Semaphore作为Java并发工具类,不同于synchronized和ReentrantLock,它能允许多个线程并发访问。Semaphore通过构造函数设定许可数量,并提供acquire()和release()方法来获取和释放许可。在实际应用中,Semaphore可用于限流、控制并发线程数等场景。本文详细介绍了Semaphore的API用法及多个实用案例,包括简单使用、异常处理下的正确释放锁以及带超时限制的许可获取。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、概述

在之前学习的synchronized关键字的对象锁与ReentrantLock重入锁都只能允许一个线程获取锁。而Semaphore不同,它可以允许多个线程获取锁访问资源,Semaphore常被运用在限流等应用场景。

二、常用API

构造方法

Semaphore(int permits) : 参数表示许可数量
Semaphore(int permits,boolean fair): 当fair等于true时,设置为公平信号量,默认是非公平的

常用方法

  • void acquire() throws InterruptedException:获取一个许可,当许可数量为0的时候,当前线程阻塞,该操作可以响应中断
  • void acquireUninterruptibly(int permits) :和acquire(int permits) 方法类似,只是不会响应线程中断
  • boolean tryAcquire():尝试获取1个许可,不管是否能够获取成功,都立即返回,true表示获取成功,false表示获取失败
  • boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException:尝试在指定的时间内获取1个许可,获取成功返回true,指定的时间过后还是无法获取许可,返回false
  • void release():释放一个许可,将其返回给信号量
  • int availablePermits():当前可用的许可数

三、案例

1.简单使用

public class Demo1 {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 10; i++) {
         Thread t =  new Thread(()->{
                try {
                    System.out.println(Thread.currentThread().getName()+"等待获取许可");
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"获取许可,执行业务逻辑5秒");
                    TimeUnit.SECONDS.sleep(5);
                    System.out.println(Thread.currentThread().getName()+"释放许可");
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
         t.setName("线程"+i);
         t.start();
        }
    }
}

执行效果:

线程0等待获取许可
线程0获取许可,执行业务逻辑5秒
线程1等待获取许可
线程1获取许可,执行业务逻辑5秒
线程2等待获取许可
线程2获取许可,执行业务逻辑5秒
线程3等待获取许可
线程4等待获取许可
线程5等待获取许可
线程6等待获取许可
线程7等待获取许可
线程8等待获取许可
线程9等待获取许可
线程0释放许可
线程1释放许可
线程2释放许可
线程3获取许可,执行业务逻辑5秒
线程4获取许可,执行业务逻辑5秒
线程5获取许可,执行业务逻辑5秒
线程3释放许可
线程5释放许可
线程4释放许可
线程6获取许可,执行业务逻辑5秒
线程7获取许可,执行业务逻辑5秒
线程8获取许可,执行业务逻辑5秒
线程7释放许可
线程6释放许可
线程8释放许可
线程9获取许可,执行业务逻辑5秒
线程9释放许可

可以看到每次最多同时只有三个线程能执行代码块。

2. 正确释放锁

在上述的例子中,如果在执行逻辑代码过程中遇到了异常,就不能释放锁,将会导致排队的线程永远得不到许可。

 Thread t =  new Thread(()->{
             boolean flag = false;
                try {
                    System.out.println(Thread.currentThread().getName()+"等待获取许可");
                    semaphore.acquire();
                    flag = true;
                    System.out.println(Thread.currentThread().getName()+"获取许可,执行业务逻辑5秒");
                    TimeUnit.SECONDS.sleep(5);
                    System.out.println(Thread.currentThread().getName()+"释放许可");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    if (flag)
                    semaphore.release();
                }
            });

使用一个boolean类型的变量flag控制,是为了防止在acquire()过程中发生异常,也进行锁的释放,从而对导致信号量的许可数量增加

3.在规定时间内获取许可

信号量的tryAcquire()方法,可以设置超时等待时间

public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 10; i++) {
         Thread t =  new Thread(()->{
             boolean flag = false;
                try {
                    System.out.println(Thread.currentThread().getName()+"等待获取许可");
                    flag = semaphore.tryAcquire(2,TimeUnit.SECONDS);
                    if (flag){
                        System.out.println(Thread.currentThread().getName()+"获取许可,执行业务逻辑6秒");
                        TimeUnit.SECONDS.sleep(6);
                    }else {
                        System.out.println(Thread.currentThread().getName()+"获取许可超时,失败");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    if (flag){
                        System.out.println(Thread.currentThread().getName()+"释放许可");
                        semaphore.release();
                    }
                }
            });
         t.setName("线程"+i);
         t.start();
         TimeUnit.SECONDS.sleep(1);
        }
    }
}

执行效果:

线程0等待获取许可
线程0获取许可,执行业务逻辑6秒
线程1等待获取许可
线程1获取许可,执行业务逻辑6秒
线程2等待获取许可
线程2获取许可,执行业务逻辑6秒
线程3等待获取许可
线程4等待获取许可
线程3获取许可超时,失败
线程5等待获取许可
线程0释放许可
线程4获取许可,执行业务逻辑6秒
线程6等待获取许可
线程1释放许可
线程5获取许可,执行业务逻辑6秒
线程7等待获取许可
线程2释放许可
线程6获取许可,执行业务逻辑6秒
线程8等待获取许可
线程7获取许可超时,失败
线程9等待获取许可
线程8获取许可超时,失败
线程9获取许可超时,失败
线程4释放许可
线程5释放许可
线程6释放许可
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值