Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Semaphore使用方法

Semaphore使用方法

作者头像
benym
发布于 2023-02-14 08:42:35
发布于 2023-02-14 08:42:35
1.3K00
代码可运行
举报
文章被收录于专栏:后端知识体系后端知识体系
运行总次数:0
代码可运行

# Semaphore使用方法

Semaphore可以翻译为信号量,Semaphore可以控制同时访问的线程个数,通过acquire()获取一个许可,如果没有许可就等待,release()方法则可以释放一个许可

# 构造方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    /**
     * 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则线程为先进先出
# 常用方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void acquire() throws InterruptedException {  }     //获取一个许可
public void acquire(int permits) throws InterruptedException { }    //获取permits个许可
public void release() { }          //释放一个许可
public void release(int permits) { }    //释放permits个许可

acquire()用来获取一个许可,若无许可能够获得,则会一直等待,直到获得许可

release()用来释放许可。注意,在释放许可之前,必须先获获得许可

这4个方法都会被阻塞,如果想立即执行得到结果,可以使用以下方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public boolean tryAcquire() { };    
//尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { };  
//尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public boolean tryAcquire(int permits) { }; 
//尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { }; 

另外还可以通过availablePermits()方法得到可用的许可数目。

# 使用案例

**案例一:**假若一个工厂有5台机器,但是有8个工人,一台机器同时只能被一个工人使用,只有使用完了,其他工人才能继续使用。那么我们就可以通过Semaphore来实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Test {

    public static void main(String[] args) {
        // 工人数目
        int n = 8;
        // 机器数目
        Semaphore semaphore = new Semaphore(5);
        for (int i = 0; i < n; i++) {
            int finalI = i;
            new Thread(() -> {
                try {
                    semaphore.acquire();
                    System.out.println("工人" + finalI + "占用一个机器在生产");
                    Thread.sleep(2000);
                    System.out.println("工人" + finalI + "释放出机器");
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

在获取许可之后,我们sleep一下当前线程,让他不要那么快进行释放,观察运行的结果

运行结果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
工人0占用一个机器在生产
工人3占用一个机器在生产
工人2占用一个机器在生产
工人1占用一个机器在生产
工人4占用一个机器在生产
工人3释放出机器
工人4释放出机器
工人0释放出机器
工人1释放出机器
工人2释放出机器
工人6占用一个机器在生产
工人5占用一个机器在生产
工人7占用一个机器在生产
工人6释放出机器
工人7释放出机器
工人5释放出机器

可以发现,当规定Semaphorepermits为5时,最多有5个线程获取许可,剩余的线程必须等待许可释放之后才能获取许可

**案例二:**流量控制

Semaphore可以用于做流量控制,特别是公用资源有限的应用场景,比如数据库连接。假如有一个需求, 要读取几万个文件的数据,因为都是 IO 密集型任务,我们可以启动几十个线程并发地读取,但是如果读到内存后,还需要存储到数据库中,而数据库的连接数只有 10 个,这时我们必须控制只有 10 个线程同时获取数据库连接保存数据,否则会报错无法获取数据库连接。这个时候,就可以使用 Semaphore 来做流量控制。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class ConnectionSemaphore {

    private final static int CONNECTION_SIZE = 10;
    // 两个信号量,分别表示可用连接和已用连接
    private final Semaphore userFulLink, useLessLink;
    // 存放数据库链接的容器,这里用Integer代替
    private static final LinkedList<Integer> connectionPool = new LinkedList<>();

    static {
        for (int i = 0; i < CONNECTION_SIZE; i++) {
            connectionPool.addLast(i);
        }
    }

    public ConnectionSemaphore(Semaphore userFulLink, Semaphore useLessLink) {
        this.userFulLink = userFulLink;
        this.useLessLink = useLessLink;
    }

    /*归还连接*/
    public void returnConnect(Integer connection) throws InterruptedException {
        if (connection != null) {
            System.out.println(
                    "当前有" + userFulLink.getQueueLength() + "个线程等待数据库连接," + "可用连接数:" + userFulLink
                            .availablePermits());
            useLessLink.acquire();
            synchronized (connectionPool) {
                connectionPool.addLast(connection);
            }
            userFulLink.release();
        }
    }

    /*从池子拿连接*/
    public Integer takeConnect() throws InterruptedException {
        userFulLink.acquire();
        Integer connection;
        synchronized (connectionPool) {
            connection = connectionPool.removeFirst();
        }
        useLessLink.release();
        return connection;
    }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 测试类
 */
public class Test {

    private static ConnectionSemaphore connectionSemaphore = new ConnectionSemaphore();

    private static class testThread implements Runnable {

        @Override
        public void run() {
            // 模拟每个线程的不同持有时间
            Random randomTime = new Random();
            long start = System.currentTimeMillis();
            try {
                Integer connect = connectionSemaphore.takeConnect();
                System.out.println(Thread.currentThread().getName()
                        + "_获取数据库连接共耗时【" + (System.currentTimeMillis() - start) + "】ms.");
                // 模拟业务,线程持有连接查询数据
                Thread.sleep(100 + randomTime.nextInt(100));
                System.out.println(Thread.currentThread().getName()+"_查询数据完成,释放连接");
                connectionSemaphore.returnConnect(connect);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 15; i++) {
            Thread thread = new Thread(new testThread());
            thread.start();
        }
    }
}

运行结果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Thread-14_获取数据库连接共耗时【0】ms.
Thread-13_获取数据库连接共耗时【0】ms.
Thread-8_获取数据库连接共耗时【0】ms.
Thread-0_获取数据库连接共耗时【1】ms.
Thread-10_获取数据库连接共耗时【0】ms.
Thread-11_获取数据库连接共耗时【0】ms.
Thread-6_获取数据库连接共耗时【0】ms.
Thread-12_获取数据库连接共耗时【0】ms.
Thread-1_获取数据库连接共耗时【2】ms.
Thread-9_获取数据库连接共耗时【0】ms.
Thread-0_查询数据完成,释放连接
Thread-14_查询数据完成,释放连接
当前有5个线程等待数据库连接,可用连接数:0
当前有5个线程等待数据库连接,可用连接数:0
Thread-7_获取数据库连接共耗时【180】ms.
Thread-9_查询数据完成,释放连接
当前有3个线程等待数据库连接,可用连接数:0
Thread-4_获取数据库连接共耗时【179】ms.
Thread-5_获取数据库连接共耗时【181】ms.
Thread-8_查询数据完成,释放连接
当前有2个线程等待数据库连接,可用连接数:0
Thread-3_获取数据库连接共耗时【185】ms.
Thread-10_查询数据完成,释放连接
当前有1个线程等待数据库连接,可用连接数:0
Thread-2_获取数据库连接共耗时【190】ms.
Thread-12_查询数据完成,释放连接
Thread-1_查询数据完成,释放连接
当前有0个线程等待数据库连接,可用连接数:0
当前有0个线程等待数据库连接,可用连接数:0
Thread-13_查询数据完成,释放连接
当前有0个线程等待数据库连接,可用连接数:2
Thread-6_查询数据完成,释放连接
当前有0个线程等待数据库连接,可用连接数:3
Thread-11_查询数据完成,释放连接
当前有0个线程等待数据库连接,可用连接数:4
Thread-3_查询数据完成,释放连接
当前有0个线程等待数据库连接,可用连接数:5
Thread-2_查询数据完成,释放连接
当前有0个线程等待数据库连接,可用连接数:6
Thread-5_查询数据完成,释放连接
当前有0个线程等待数据库连接,可用连接数:7
Thread-7_查询数据完成,释放连接
当前有0个线程等待数据库连接,可用连接数:8
Thread-4_查询数据完成,释放连接
当前有0个线程等待数据库连接,可用连接数:9

从打印结果可以看出,一次只有 10 个线程执行acquire(),只有线程进行release()方法后才会有别的线程执行acquire()。看到这里或许会疑惑在takeConnect中获取许可的是可用连接userFulLink,而释放许可的是useLessLink,在Semaphore中的release方法原本的注释如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    /**
     * Releases a permit, returning it to the semaphore.
     *
     * <p>Releases a permit, increasing the number of available permits by
     * one.  If any threads are trying to acquire a permit, then one is
     * selected and given the permit that was just released.  That thread
     * is (re)enabled for thread scheduling purposes.
     *
     * <p>There is no requirement that a thread that releases a permit must
     * have acquired that permit by calling {@link #acquire}.
     * Correct usage of a semaphore is established by programming convention
     * in the application.
     */
    public void release() {
        sync.releaseShared(1);
    }

释放许可证,将其返回给信号量。释放许可证,将可用许可证的数量增加一。如果任何线程试图获得许可,则选择一个线程并给予刚刚释放的许可。该线程(重新)启用用于线程调度目的。不要求释放许可的线程必须通过调用{@link acquire} 获得该许可。信号量的正确使用是通过应用程序中的编程约定建立的。

这句话的意思就是说,release方法仅仅只是把许可证数量加一,在release之前不需要对应的信号量去执行acquire,那么这段代码的含义就可以理解为减少可用连接的数量,增加已用连接的数量,因为当一个线程持有连接之后可用连接应该-1,而已用的连接数应该+1,当一个线程执行完毕业务之后应该将已用连接-1,可用连接+1。

需要注意的是:Semaphore只是对资源并发访问的线程数进行监控,并不会保证线程安全。

# 参考文章

https://www.cnblogs.com/dolphin0520/p/3920397.html https://www.jianshu.com/p/0d53a643a60c

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-01-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
控制并发线程数的Semaphore信号量(Semaphore)语法应用场景其他方法
信号量(Semaphore) 一个同步对象,用于保持在0至指定最大值之间的一个计数值。 当线程完成一次对该semaphore对象的等待(wait)时,该计数值减一 当线程完成一次对semaphore对象的释放(release)时,计数值加一 当计数值为0,则线程等待该semaphore对象不再能成功直至该semaphore对象变成signaled状态。 semaphore对象的计数值大于0,为signaled状态 计数值等于0,为nonsignaled状态 semaphore对象适用于控制一个仅支持有限个
JavaEdge
2018/05/16
1.9K0
并发设计模式实战系列(17):信号量(Semaphore)
今天为大家带来的是并发设计模式实战系列,第十七章信号量(Semaphore),废话不多说直接开始~
摘星.
2025/05/20
1660
Java 并发(6)Semaphore 源码分析
Semaphore 提供了一个许可证的概念,可以把这个许可证看作公共汽车车票,只有成功获取车票的人才能够上车,并且车票是有一定数量的,不可能毫无限制的发下去,这样就会导致公交车超载。所以当车票发完的时候 (公交车以满载),其他人就只能等下一趟车了。如果中途有人下车,那么他的位置将会空闲出来,因此如果这时其他人想要上车的话就又可以获得车票了。
JavaFish
2020/04/07
2880
JUC 常用4大并发工具类
JUC就是java.util.concurrent包,这个包俗称JUC,里面都是解决并发问题的一些东西
彼岸舞
2020/11/06
5470
Java多线程六脉神剑-少冲剑-Semaphore
少冲剑:少冲剑轻灵迅速,Semaphore 用于控制资源的访问数量,操作灵活迅速。
一杯茶Ja
2024/09/28
1340
并发编程之Semaphore
一、简介 Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。很多年以来,我都觉得从字面上很难理解Semaphore所表达的含义,只能把它比作是控制流量的红绿灯,比如XX马路要限制流量,只允许同时有一百辆车在这条路上行使,其他的都必须在路口等待,所以前一百辆车会看到绿灯,可以开进这条马路,后面的车会看到红灯,不能驶入XX马路,但是如果前一百辆中有五辆车已经离开了XX马路,那么后面就允许有5辆车驶入马路,这个例子里说的车就是线程,驶入马路就表示线程
lyb-geek
2018/03/27
7510
Java同步组件之CountDownLatch,Semaphore
Java同步组件概况 CountDownLatch : 是闭锁,通过一个计数来保证线程是否一直阻塞 Semaphore: 控制同一时间,并发线程数量 CyclicBarrier:字面意思是回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。 ReentrantLock:是一个重入锁,一个线程获得了锁之后仍然可以反复加锁,不会出现自己阻塞自己的情况。 Condition:配合ReentrantLock,实现等待/通知模型 FutureTask:FutureTask实现了接口Future,同Fu
开源日记
2021/02/05
5380
呵,Semaphore ,就这?
这是并发线程工具类的第二篇文章,在第一篇中,我们分析过 CountDownLatch 的相关内容,你可以参考
cxuan
2021/04/21
6370
呵,Semaphore ,就这?
Java并发工具 - 使用Semaphore实现线程同步
Java中的并发工具之一是Semaphore(信号量),它可以用于实现线程之间的同步和互斥。下面将详细介绍Semaphore的概念、用法和示例,以帮助您理解如何使用Semaphore来实现线程同步。
用户1289394
2024/04/15
3670
Java并发工具 - 使用Semaphore实现线程同步
并发控制利器Semaphore
Semaphore 是Java并发编程中的一个重要工具,用于管理对共享资源的访问权限,确保系统资源不会因过度访问而耗尽。形象地说,Semaphore 可以比喻为交通信号灯,它控制着能够同时进入特定区域(如马路)的车辆数(线程数)。当一定数量的车辆(线程)进入后,其余车辆必须等待,直到有车辆离开,空出“车位”(许可证)为止。在编程中,Semaphore 通过协调线程访问,保证公共资源的合理分配。
终有链响
2024/07/29
1340
并发控制利器Semaphore
Java并发编程:CountDownLatch、CyclicBarrier和Semaphore
Java并发编程:CountDownLatch、CyclicBarrier和Semaphore
yaphetsfang
2020/07/30
5470
Semaphore源码分析-Java快速入门教程
semaphore是一个计数信号量,用于控制同时访问某个资源的线程数量。Semaphore可以用于限制同时访问某个资源的线程数量,或者用于保护某个重要资源,以避免多个线程同时修改它,从而导致数据不一致的问题。在Java中Semaphore是用于实现线程同步的一个类,它提供了一种基于许可证的机制来控制并发访问的数量。下面对Semaphore.java进行分析:
jack.yang
2025/04/05
890
Semaphore源码分析-Java快速入门教程
信号量
Hystrix内部提供了两种模式执行逻辑:信号量和线程池。默认情况下,Hystrix使用线程池模式。那什么是信号量呢?
只喝牛奶的杀手
2019/08/26
6780
深入解析Java并发包(JUC)中的Semaphore
码到三十五 : 个人主页 心中有诗画,指尖舞代码,目光览世界,步履越千山,人间尽值得 !
公众号:码到三十五
2024/03/24
3970
深入解析Java并发包(JUC)中的Semaphore
Java核心知识点整理大全9-笔记
4.1.14.5. PriorityBlockingQueue(compareTo 排序实现优先)
用户10920956
2024/01/18
1230
Java核心知识点整理大全9-笔记
并发工具箱 concurrent包的原理分析以及使用
BlockingQueue 通常用于一个线程生产对象,而另外一个线程消费这些对象的场景。下图是对这个原理的阐述:
小勇DW3
2018/08/30
8690
并发工具箱 concurrent包的原理分析以及使用
AQS、CountDownLatch、CyclicBarrier、Semaphore、Exchanger
在编程的竞技场中,多线程与并发是每一级大牛必备的技能!今天,就让我们深入了解Java并发武器库中的“五神兵”——AQS、CountDownLatch、CyclicBarrier、Semaphore、Exchanger的强大之处。他们如棋盘上的棋子,既能彼此协调,又能独当一面,解决了无数线程之问的冲突、同步与协作难题。
疯狂的KK
2024/06/04
1670
AQS、CountDownLatch、CyclicBarrier、Semaphore、Exchanger
彻底理解Java并发:Java并发工具类
CountDownLatch 线程计数器,俗称闭锁,作用是类似加强版的 Join,是让一组线程等待其他的线程完成工作以后才执行
栗筝i
2022/12/01
6080
JUC并发包系列:Semaphore
Semaphore是一个有效的流量控制工具,它基于AQS共享锁实现。我们常常用它来控制对有限资源的访问。
张云飞Vir
2022/09/29
6540
JDK源码分析-Semaphore
Semaphore 是并发包中的一个工具类,可理解为信号量。通常可以作为限流器使用,即限制访问某个资源的线程个数,比如用于限制连接池的连接数。
WriteOnRead
2019/08/16
4230
JDK源码分析-Semaphore
相关推荐
控制并发线程数的Semaphore信号量(Semaphore)语法应用场景其他方法
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验