Semaphore详解


前言

Semaphore(信号量)是一种用于多线程编程的同步工具,用于控制同时访问某个资源的线程数量
在这里插入图片描述

Semaphore维护了一个计数器,线程可以通过调用acquire()方法来获取Semaphore中的许可证,当计数器为0时,调用acquire()的线程将被阻塞,直到有其他线程释放许可证;线程可以通过调用release()方法来释放Semaphore中的许可证,这会使Semaphore中的计数器增加,从而允许更多的线程访问共享资源。

常用API

构造器

    /**
     * Creates a {@code Semaphore} with the given number of
     * permits and nonfair fairness setting.
     *
     * @param permits the initial number of permits available.
     *        This value may be negative, in which case releases
     *        must occur before any acquires will be granted.
     */
    public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }

    /**
     * Creates a {@code Semaphore} with the given number of
     * permits and the given fairness setting.
     *
     * @param permits the initial number of permits available.
     *        This value may be negative, in which case releases
     *        must occur before any acquires will be granted.
     * @param fair {@code true} if this semaphore will guarantee
     *        first-in first-out granting of permits under contention,
     *        else {@code false}
     */
    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }
  • permits 表示许可证的数量(资源数)
  • fair 表示公平性,如果这个设为 true 的话,下次执行的线程会是等待最久的线程

常用方法

  • acquire() 表示阻塞并获取许可
  • tryAcquire() 方法在没有许可的情况下会立即返回 false,要获取许可的线程不会阻塞
  • release() 表示释放许可

Semaphore使用

1.Semaphore实现服务接口限流

代码如下(示例):

public class SemaphoreDemo {
    private static Semaphore semaphore = new Semaphore(2);
    private static ExecutorService executor = Executors.newFixedThreadPool(10);


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        for(int i = 0;i<10;i++){
            executor.submit(()->getProductInfo());
        }
        executor.shutdown();

    }

    public static String getProductInfo(){

        try {
            semaphore.acquire();
            System.out.println("请求服务");
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }finally {
            semaphore.release();
        }
        System.out.println(Thread.currentThread().getName()+"释放资源");
        return "商品详情信息";
    }

    public static String getProductInfo2(){

        if(!semaphore.tryAcquire()){
            System.out.println("请求被流控了");
            return "请求被流控了";
        }
        try {
            System.out.println("请求服务");
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }finally {
            semaphore.release();
        }
        System.out.println(Thread.currentThread().getName()+"释放资源");
        return "商品详情信息";
    }

}
/**
 * getProductInfo:
 * 请求服务
 * 请求服务
 * 请求服务
 * 请求服务
 * pool-1-thread-4释放资源
 * pool-1-thread-5释放资源
 * 请求服务
 * pool-1-thread-2释放资源
 * pool-1-thread-8释放资源
 * 请求服务
 * 请求服务
 * pool-1-thread-3释放资源
 * 请求服务
 * pool-1-thread-7释放资源
 * pool-1-thread-1释放资源
 * 请求服务
 * 请求服务
 * pool-1-thread-6释放资源
 * pool-1-thread-10释放资源
 * pool-1-thread-9释放资源
 *
 *
 *
 * getProductInfo2:
 *
 * 请求服务
 * 请求被流控了
 * 请求被流控了
 * 请求被流控了
 * 请求被流控了
 * 请求服务
 * 请求被流控了
 * 请求被流控了
 * 请求被流控了
 * 请求被流控了
 * pool-1-thread-4释放资源
 * pool-1-thread-2释放资源
 */

1.Semaphore实现数据库连接池

代码如下(示例):

public class SemaphoreDemo2 {

    private static ConnectPool pool = new ConnectPool(2);
    private static ExecutorService executor = Executors.newCachedThreadPool();


    public static void main(String[] args) {

        for(int i = 0;i<5;i++){
            final int id = i + 1;
            executor.execute(()->{
                Connect connect = null;
                try {
                    System.out.println("线程" + id + "等待获取数据库连接");
                    connect = pool.openConnect();
                    System.out.println("线程" + id + "已拿到数据库连接:" + connect);
                    Thread.sleep(2000);
                    System.out.println("线程" + id + "释放数据库连接:" + connect);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }finally {
                    pool.releaseConnect(connect);
                }
            });
        }
    }

}

class ConnectPool{
    private int size;
    private Connect[] connects;

    private boolean[] connectFlag;
    private Semaphore semaphore;

    public ConnectPool(int size){
        this.size = size;
        semaphore = new Semaphore(size,true);
        connects = new Connect[size];
        connectFlag = new boolean[size];
        initConnects();
    }

    private void initConnects(){
        for(int i = 0;i<this.size;i++){
            connects[i] = new Connect();
        }
    }

    public Connect openConnect() throws InterruptedException {
        semaphore.acquire();
        return getConnect();
    }

    private synchronized Connect getConnect(){
        for(int i = 0;i<connectFlag.length;i++){
            if(!connectFlag[i]){
                connectFlag[i] = true;
                return connects[i];
            }
        }
        return null;
    }

    public synchronized void releaseConnect(Connect connect){
        for(int i = 0;i<this.size;i++){
            if(connect == connects[i]){
                connectFlag[i] = false;
                semaphore.release();
            }
        }
    }
}

/**
 * 数据库连接
 */
class Connect{
    private static int count = 1;
    private int id = count++;

    public Connect(){
        //假设打开一个连接需要1s
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("连接#"+id+"#");
    }

    @Override
    public String toString(){
        return "#"+id+"#";
    }
}
/**
 * 连接#1#
 * 连接#2#
 * 线程3等待获取数据库连接
 * 线程1等待获取数据库连接
 * 线程2等待获取数据库连接
 * 线程4等待获取数据库连接
 * 线程5等待获取数据库连接
 * 线程1已拿到数据库连接:#2#
 * 线程3已拿到数据库连接:#1#
 * 线程1释放数据库连接:#2#
 * 线程2已拿到数据库连接:#2#
 * 线程3释放数据库连接:#1#
 * 线程4已拿到数据库连接:#1#
 * 线程2释放数据库连接:#2#
 * 线程5已拿到数据库连接:#2#
 * 线程4释放数据库连接:#1#
 * 线程5释放数据库连接:#2#
 */

应用场景总结

以下是一些使用Semaphore的常见场景:

  1. 限流:Semaphore可以用于限制对共享资源的并发访问数量,以控制系统的流量。
  2. 资源池:Semaphore可以用于实现资源池,以维护一组有限的共享资源。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值