控制并发线程数-Semaphore(信号量)的简单使用

22 篇文章 0 订阅
8 篇文章 0 订阅

Semaphore(信号量)用来控制并发访问的线程数量,它通过协调各个线程来保证资源的合理使用。其内部使用的是AQS机制(concurrent包下很多类实现原理都是基于AQS)。Semaphore实现控制并发线程数可以抽象为停车场模型,一个固定车位的停车场,当车位满了,便不再允许新的车辆进入;若当前车库驶出多少辆,则就允许进入多少辆。Semaphore做的就是监控车位大小功能。通过构造函数new Semaphore(N)来实现,N表示最多并发访问的线程数。所以当初始化创建了5个许可证时,如果多余5个线程并发访问则阻塞排队等候,至于是否公平访问,可以灵活的设置new Semaphore(int permits,boolean fair),当前线程acquire()一次许可证就减少一个,release()一次就归还一个。

Semaphore应用场景


例如有些方法很耗时:需要一次性读取10万条数据,并按照某些规则进行计算,然后写入csv文件中,提供给管理员下载,由于这种方法很耗机器资源,就需要对这个方法进行限制同时调用的线程数,下面以50个管理员并发调用为例,每次至多允许3个人同时调用:

/**
 * @desc:信号量测试:50个管理员同时调用某个很耗时的接口,至多允许三个管理员同时调用,其余的排队等候
 **/
public class SemaphoreTest {
 
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3, true);
        ExecutorService executor = Executors.newFixedThreadPool(50);
        for (int i = 0; i < 50; i++) {
            executor.execute(() -> {
                try {
                    semaphore.acquire();
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + "号管理员获取许可,任务开始");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                    System.out.println(Thread.currentThread().getName() + "号管理员释放许可,任务结束");
                }
            });
        }
        executor.shutdown();
    }
}
 
//输出结果:
pool-1-thread-2号管理员获取许可,任务开始
pool-1-thread-3号管理员获取许可,任务开始
pool-1-thread-1号管理员获取许可,任务开始
pool-1-thread-3号管理员释放许可,任务结束
pool-1-thread-2号管理员释放许可,任务结束
pool-1-thread-1号管理员释放许可,任务结束
pool-1-thread-5号管理员获取许可,任务开始
pool-1-thread-6号管理员获取许可,任务开始
pool-1-thread-4号管理员获取许可,任务开始
……

例如IO密集型操作,开启了30个工作线程并发的读取文件,然后进行持久化操作,但是数据库最大连接数为20,如果不控制,就出现获取数据库连接失败错误,通过Semaphore控制并发连接数据库线程数:

public class SemaphoreTest {
    static ExecutorService executor = Executors.newFixedThreadPool(30);
    static Semaphore semaphore = new Semaphore(20, true);
 
    public static void main(String[] args) {
        for (int i = 0; i < 30; i++) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    semaphore.tryAcquire();  //获取许可证
                    // TODO something  work
                    System.out.println("current thread " + Thread.currentThread().getId() + " time=" + System.currentTimeMillis());
                    semaphore.release();  //归还许可证
                }
            });
        }
        executor.shutdown();
    }
}

需要注意的是,acquire()允许一次性获取多个信号量,比如semaphore.acquire(3)表示一次性获取3个,但是在释放的时候也得释放三个semaphore.release(3),否则许可证就越来越少了。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值