二十八. Semaphore的使用详解

2023-05-16

前言

Semaphore的官方注释如下。

计数信号量。从概念上讲,信号量维护一组许可证(permits)。通常,每次调用Semaphore#acquire方法时如果已经没有许可证,则会阻塞线程,直到获取到许可证。每次调用Semaphore#release方法都会添加一个许可证,此时某个阻塞的线程就会拿到许可证并解除阻塞状态。

Semaphore在初始化时需要指定许可证数,Semaphore#acquire方法会尝试获取一定数量的许可证,若许可证数量不足,则当前线程进入阻塞状态。相应地,获取到了许可证的线程,在执行完毕后,需要调用Semaphore#release方法来归还许可证。有以下两点需要注意。

  1. 建议在finally代码块中调用Semaphore#release方法,确保许可证的归还;
  2. 没有拿到许可证的线程,也可以调用Semaphore#release方法。故需要在业务代码层面保证Semaphore的正确使用。

正文

一. api整理

Semaphore的常用api如下表所示。

api说明
Semaphore(int permits)构造函数。permits指定初始许可证的数量,此时Semaphore的工作方式是非公平锁的方式
Semaphore(int permits, boolean fair)构造函数。permits指定初始许可证的数量,fair指定Semaphore是公平锁还是非公平锁的工作方式
void acquire()获取许可证。默认获取1张许可证,获取失败则当前线程进入阻塞状态,且响应中断
void acquire(int permits)获取许可证。permits指定需要获取的许可证数量,获取失败则当前线程进入阻塞状态,且响应中断
void release()归还许可证。默认归还1张许可证
void release(int permits)归还许可证。permits指定归还许可证的数量
boolean tryAcquire()尝试获取许可证。获取成功返回true,获取失败返回false。就算Semaphore的工作方式是公平锁方式,但是该方法在被调用的那一刻,也是会以非公平锁的方式尝试去获取许可证
boolean tryAcquire(long timeout, TimeUnit unit)尝试在一段时间内获取许可证。获取成功返回true,获取失败返回false。该方法会尝试获取许可证,如果没有许可证,则等待timeout的时间,等待期间响应中断,如果等待时间到,也没有获取到许可证,则返回false

有一点需要注意,就是无论Semaphore公平锁还是非公平锁的工作方式,tryAcquire()方法会在调用的那一刻,以非公平锁的方式(就是不管是否已经有其它线程正在等待获取许可证)去获取许可证。如果想要tryAcquire()方法以公平锁的方式去获取许可证,则方法如下。

  1. Semaphore设置为公平锁的工作方式;
  2. 调用tryAcquire(0, TimeUnit.SECONDS)方法。

二. 公平锁方式的Semaphore的简单使用

公平锁方式的Semaphore能够保证在没有许可证时,最先等待获取许可证的线程一定能够在有许可证时最先获取到许可证。示例如下所示。

public class SemaphoreTest {

    @Test
    public void 公平锁方式的简单使用() throws Exception {
        // 创建Semaphore对象,且指定公平锁的工作方式
        Semaphore semaphore = new Semaphore(1, true);

        // 创建一个简单任务
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + " 执行了");
                } catch (InterruptedException e) {
                    // ignore
                } finally {
                    semaphore.release();
                }
            }
        };

        // 创建多个线程来执行同一个任务
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(runnable, "线程" + i);
            thread.start();
            // 放弃时间片,确保上一线程已经拿到许可证或者已经进入阻塞状态
            for (int k = 0; k < 100; k++) {
                Thread.yield();
            }
        }

        // 主线程睡眠1秒,以便观察现象
        Thread.sleep(1000);
    }

}

上述示例演示了在某一刻,同步队列中有等待获取许可证的线程,以及有正在调用acquire()方法获取许可证的线程,此时一定是同步队列中等待获取许可证最久的线程能够获取到许可证。运行测试程序,结果如下。

在这里插入图片描述

三. 非公平锁方式的Semaphore的简单使用

非公平锁方式的Semaphore不能够保证在没有许可证时,最先等待获取许可证的线程一定能够在有许可证时最先获取到许可证。示例如下所示。

public class SemaphoreTest {

    @Test
    public void 非公平锁方式的简单使用() throws Exception {
        // 创建Semaphore对象,且指定非公平锁的工作方式
        Semaphore semaphore = new Semaphore(1, false);

        // 创建一个简单任务
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + " 执行了");
                } catch (InterruptedException e) {
                    // ignore
                } finally {
                    semaphore.release();
                }
            }
        };

        // 创建多个线程来执行同一个任务
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(runnable, "线程" + i);
            thread.start();
            // 放弃时间片,确保上一线程已经拿到许可证或者已经进入阻塞状态
            for (int k = 0; k < 100; k++) {
                Thread.yield();
            }
        }

        // 主线程睡眠1秒,以便观察现象
        Thread.sleep(1000);
    }

}

上述示例演示了在某一刻,同步队列中有等待获取许可证的线程,以及有正在调用acquire()方法获取许可证的线程,此时会出现调用acquire()方法的线程比同步队列中等待最久的线程先获取到许可证的情况。运行测试程序,结果如下。

在这里插入图片描述

总结

关于Semaphore的使用,总结如下。

  1. Semaphore有两种工作方式,第一种是公平锁的工作方式,第二种是非公平锁的工作方式;
  2. 初始化Semaphore时需要指定初始许可证的数量permits,这个数量可以为负数,但是如果初始许可证数量指定为负数,那么需要保证在acquire()方法调用前先调用release()方法来填充许可证数量;
  3. 调用Semaphore#acquire方法能够获取许可证,获取到许可证的线程会从Semaphore#acquire方法立即返回,从而可以继续往下执行,而获取失败的线程就会阻塞在Semaphore#acquire方法上,直到获取到许可证,或者线程被中断;
  4. 调用Semaphore#release方法能够归还许可证,此时如果有线程正在等待获取许可证,那么其中一个线程能够获取到许可证并从等待中返回;
  5. Semaphore#release方法调用时,并不要求线程已经获取到许可证;
  6. Semaphore还提供了tryAcquire()方法来让线程在获取许可证失败时不进入阻塞状态而是直接返回false
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

二十八. Semaphore的使用详解 的相关文章

  • fork()使用详解

    其他参考 xff1a linux中fork xff08 xff09 函数详解 一 fork入门知识 进程的定义 xff1a 进程是一个执行中的程序的实例 xff0c 是系统进行资源分配和调度的一个独立单位 PCB是进程存在的唯一标识 PCB
  • Qt QMessageBox使用详解

    本文详细的介绍了QMessageBox控件的各种操作 xff0c 例如 xff1a 消息提示框的使用 判断消息提示框的按钮 标准图标和自定义图标 定时关闭 自定义样式等操作 本文作者原创 xff0c 转载请附上文章出处与本文链接 Qt QM
  • axios 使用详解

    一 安装 cnpm install axios 二 使用 三种写法 span class token comment 第一种写法 span axios span class token punctuation span span class
  • Java中Semaphore(信号量)的使用

    Semaphore的作用 xff1a 在java中 xff0c 使用了synchronized关键字和Lock锁实现了资源的并发访问控制 xff0c 在同一时间只允许唯一了线程进入临界区访问资源 读锁除外 xff0c 这样子控制的主要目的是
  • Netty框架之Selector使用详解

    谈到Selector的具体使用 xff0c 那么就要结合BIO NIO的知识讲解 xff0c Selector使用在非阻塞模式NIO场景下 xff0c 学习NIO之前先要了解BIO原理 xff0c 下面我们一步步讲解 一 BIO Block
  • Postman使用详解

    一 Postman背景介绍 用户在开发或者调试网络程序或者是网页B S模式的程序的时候是需要一些方法来跟踪网页请求的 xff0c 用户可以使用一些网络的监视工具比如著名的Firebug等网页调试工具 今天给大家介绍的这款网页调试工具不仅可以
  • Semaphore 源码分析

    需要提前了解的知识点 AbstractQueuedSynchronizer 实现原理 类介绍 Semaphore 信号量 是用来控制同时访问特定资源的线程数量 它通过协调各个线程 以保证合理的使用公共资源 比如控制用户的访问量 同一时刻只允
  • 为什么我的dispatch_once会陷入僵局?

    为什么我会陷入僵局 void foo static dispatch once t onceToken dispatch once onceToken self foo whatever 我预计foo第一次调用时执行两次 现有的答案都不是很
  • 在 C# 中使用信号量

    嗨 我正在尝试使用Semaphore在我的应用程序中 我已经这样声明了 class MyThread public Thread Thrd static Semaphore sem new Semaphore 2 2 public MyTh
  • Ada 中的信号量

    我得到了以下代码并要求实现一个信号量 with Ada Text IO use Ada Text IO with Id Dispenser with Semaphores use Semaphores procedure Philos is
  • 使用线程计算不同单词的数量

    目的是计算文件中不同单词的数量 更新 先前的代码已成功完成 现在我必须做同样的事情但是使用threads 天哪 我讨厌他们 此外我还想与信号量为了更好的流动 代码包含一些以前尝试中遗漏的额外内容 我正在尝试找出可以使用的内容 我一次可以读一
  • 在c中使用信号量同步两个子进程

    我必须创建一个程序来同步两个进程 每个进程只打印一个字母 这样每当我们观察程序的输出时 A and B 不大于2 所以这会被接受 BAABBAABBABA 这不会是因为它打印 4 个 B 只打印 2 个 A ABBABB 因此 对于初学者来
  • 如何在 Web Api 操作中锁定长异步调用?

    我有这样的场景 我有一个 WebApi 和一个端点 触发时会执行大量工作 大约 2 5 分钟 这是一个具有副作用的 POST 端点 我想限制执行 以便如果向此端点发送 2 个请求 不应该发生 但安全总比遗憾更好 其中一个请求将必须等待以避免
  • 可重复使用的Barrier解决方案陷入僵局?

    我一直在读 The Little Book of Semaphores 第 41 页有一个针对可重用屏障问题的解决方案 我遇到的问题是为什么它不会产生死锁情况 1 rendezvous 2 3 mutex wait 4 count 1 5
  • 计数信号量和二进制信号量之间的区别

    计数和二进制信号量有什么区别 我在某处看到的是 两者都可以控制 N 个请求资源的进程 两者都拥有自由邦 二进制信号量和计数信号量可以保护的资源数量是否有限制 两者都只允许一个进程一次使用一种资源 还有其他区别吗 上述属性是否正确 实际上 这
  • python 中的公平信号量

    python 中是否有可能有一个公平的信号量 它可以保证阻塞线程按照它们调用的顺序解除阻塞acquire 您可能必须用其他活动部件来构建一个 例如 创建一个Queue Queue 每个听众都会发布一个全新的Event 然后它会等待 当需要唤
  • 限制异步任务

    我想运行一堆异步任务 并限制在任何给定时间可以等待完成的任务数量 假设您有 1000 个 URL 并且您只想一次打开 50 个请求 但是 一旦一个请求完成 您就会打开与列表中下一个 URL 的连接 这样 每次始终打开 50 个连接 直到 U
  • Cypress:在第一次失败时中断所有测试

    如何在第一次测试失败时中断所有赛普拉斯测试 我们使用信号量为每个 PR 与 Cypress 启动完整的 e2e 测试 但这需要太多时间 我想在第一次测试失败时中断所有测试 获取完整的错误是每个开发人员在开发时的职责 如果在部署之前出现任何问
  • 使用易失性变量和信号量 - Java

    我从线程 信号量 易失变量等开始 我想知道当我使用信号量时是否有必要将变量定义为易失性 我的意思是 有 2 个线程 一个增加变量 另一个减少变量 例如 显然 在每次访问之前 我有一个互斥体 它随时控制只有一个线程正在 玩 变量 有必要定义为
  • Semaphore.wait(timeout: .now()) 的目的是什么?

    看了一些苹果代码示例 我发现了这一点 func metadataOutput output AVCaptureMetadataOutput didOutput metadataObjects AVMetadataObject from co

随机推荐

  • 字节跳动抖音电商2-2 算法 20220331

    题目 xff1a n 61 61 nums length 1 lt 61 n lt 61 104 0 lt 61 nums i lt 61 n nums 中的所有数字都 独一无二 给定一个包含 0 n 中 n 个数的数组 nums xff0
  • java 线程池执行流程源码讲解

    threadPoolExecutor execute 执行过程 public void execute Runnable command if command 61 61 null throw new NullPointerExceptio
  • spring boot 打包成jar 包在发布到服务器上

    pom xml文件 lt xml version 61 34 1 0 34 encoding 61 34 UTF 8 34 gt lt project xmlns 61 34 http maven apache org POM 4 0 0
  • JAVA Apache POI 之sax 解析10万级大数量数据

    第一步让我们来看看我们的大量数据的excel 文件 img src https img blog csdn net 20170330162913720 watermark 2 text aHR0cDovL2Jsb2cuY3Nkbi5uZXQ
  • 百度2014校园招聘笔试题武汉站三道算法设计题

    百度2014校园招聘笔试题武汉站三道算法设计题 1 给定任意一个整整数 求比这个数大且最小的不重复数 就是相邻两位不同 xff0c 例如1231 如1101就是重复数 解 xff1a 思路 xff1a 每次将给定的值加上1 xff0c 然后
  • spring data jpa 想使用EntityManager 对sql 进行处理四种方式(第四种本人改写的)

    下面看看主体的一个类 xff1a package com chinait service impl import java util List import javax persistence EntityManager import ja
  • Swagger2 (3) 集成easymock 生成mock 测试数据

    1 什么是 easy mock 2 可以集成swagger 3 我们来玩一下 首先你需要一个swagger 服务 xff1a 其次我们需要一个easy moke 网站账号 xff1a 接下来选择一个项目 点击编辑项目的配置 配置好项目信息
  • 用canvas做视频截图遇到的坑(已填坑)

    最近负责了一个后台功能的扩展 xff0c 因为没有前端 xff0c 所以客串了一把前端 xff0c 需求的内容是做一个视频截图的功能 xff0c 这期间遇到了canvas 的跨域问题 xff0c Uncaught DOMException
  • java 8 list.stream().collect Collectors.toMap 重复key 值处理

    问题描述 xff1a list 转 map 时 xff0c 首先看 两个phoneAuthUpdater 的key 都是 1 xff0c key 如果重复 xff0c 则会报这个错 可以选择第二种方法来解决 这种方式可以解决上面的问题 xf
  • mysql 时间格式转换,时区转化

    首先 xff0c 为了更好的展示 xff0c 我先把数据库里面存储的数据展示一下 xff0c 是如下图 xff1a 1 时间格式转换 时间 39 2019 01 22 15 45 06 39 转换成 unix 时间戳 select UNIX
  • 5.4 Stream Buffer

    Stream Buffer 是一种广义 Cache xff0c 主要功能是避免因为预读而造成的 Cache Pollution 问题 当采用该机制时 xff0c 处理器可以将预读的数据序列放入 Stream Buffer 中而不是放入 Ca
  • VxWorks的信号量机制分析

    VxWorks 的信号量机制分析 VxWorks 信号量是提供任务间通信 同步和互斥的最优选择 xff0c 提供任务间最快速的通信 也是提供任务间同步和互斥的主要手段 VxWorks 提供 3 种信号量来解决不同的问题 二进制信号量 xff
  • Linux系统内存、磁盘占用情况查询

    查看磁盘占用空间 df h 显示所有磁盘的使用情况 xff0c 包括磁盘的总大小 已用空间 可用空间和文件系统类型等 查看运行内存的占用情况 free m 查看进程 1 strong span style color fe2c24 ps s
  • 关于c语言的tcp通讯详细讲解

    目录 1 TCP概览 1 1 TCP基本特征 1 2 TCP通信流程基本原理 2 TCP编程的函数接口说明 3 TCP通讯测试代码 1 TCP概览 TCP全称 Transmition Control Protocol xff0c 即 xff
  • python watchdog:监控文件系统事件的Python库

    python watchdog xff1a 监控文件系统事件的Python库和shell工具 watchdog用来监控指定目录 文件的变化 xff0c 如添加删除文件或目录 修改文件内容 重命名文件或目录等 xff0c 每种变化都会产生一个
  • 关于c语言的udp通讯详细讲解

    目录 1 UDP简介 2 UDP通信流程 3 UDP的函数接口说明 4 UDP通讯测试代码 1 UDP简介 UDP全称 User Datagram Protocol xff0c 即 xff1a 用户数据报协议 是面向无连接的协议 通常 xf
  • python中关于Opencv中关于矩形的函数总结

    最近处理图像 xff0c 以前用的都是matlab xff0c 现在入手python比较慢 xff0c 这几天看到了很多命名和功能相似的函数 xff0c 作个记录总结一下 只是为了能够看下函数知道它是做什么的 xff0c 因此不会对其用法说
  • 在虚拟机中安装Ubuntu-Docker

    在虚拟机中安装Ubuntu Docker 第一步 安装虚拟机 1 安装虚拟机 xff0c 测试网络联网 图 1 安装ubuntu 图 2 设置系统时间 2 建立快照 建立快照 xff1a 快照001 安装成功 第二步 内核更新 可以通过do
  • Java中的toString()方法

    一 toString 方法介绍 toString 方法是 Object 类中的方法 xff0c toString 方法源代码如下 xff1a 1 getClass getName 返回类的全类名 包名 43 类名 2 Integer toH
  • 二十八. Semaphore的使用详解

    前言 Semaphore的官方注释如下 计数信号量 从概念上讲 xff0c 信号量维护一组许可证 xff08 permits xff09 通常 xff0c 每次调用Semaphore acquire方法时如果已经没有许可证 xff0c 则会