防止多线程同时操作一个资源,不能不学的JUC工具类: Semaphore详解

Java——使用多线程从list中不重复地取出数据并进行处理,给多线程任务添加单项任务计时和总耗时 大部分的业务都不是独立运行的,会有参数传递到业务中,如果需要多线程调用的业务有主线程的参数需要传递过去,就可以把参数写在实现了Runnable接口的业务类的私有属性中,用业务类的构造方法传参。在业务里,使用多个线程并行处理业务能够极大的加快处理速度,但是如果我们需要明确掌握每次处理业务的耗时,和所有业务处理完成的总耗时,就可以通过CountDownLatch来帮我们实现。把处理数据的方法写在处理类中重写的Runnable接口的run()方法中。在方法中调用了属性num。添加了以上属性的构造方法。 阅读详情

前言

在工作中我们经常需要考虑对资源的使用,避免资源被过度使用或者资源没有被利用到而造成的问题,那我们该如何去限制访问某些资源的线程数目,从而对完成资源的保护。

1. 限制多线程同时操作的方式

concurrent包为我们提供了多种防止多线程同时操作一个资源的方法

volatile
原子类
Synchronized和Lock
Semaphore

2. Semaphore是什么?

Semaphore被翻译为计数信号量,通常使用进行并发线程数量的限制,保证多个线程能够合理的使用资源。用大白话理解就是理解为红路灯。
官方的翻译:计数信号量。
从概念上讲,信号量维护一组许可证。 如有必要,每个acquire块都将阻塞直到获得许可为止,然后再获取它。 每个release添加一个许可证,从而有可能释放阻塞的获取者。 但是,没有使用实际的许可对象。 Semaphore只是保持可用数量的计数并采取相应措施。

3. 应用场景

公共资源有限的地方,我们就需要考虑限制的问题,防止过度的操作,带来的不良影响
例如:数据库连接

4. 如何使用Semaphore

没有进行控制的代码

public static void main(String[] args) {
    ExecutorService executorService = Executors.newFixedThreadPool(5);
    IntStream.range(0,5).forEach(i -> executorService.submit(() ->{
        try {
            System.out.println(Thread.currentThread().getName() + "gogogo");
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + "正在操作");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "释放操作");

    }));

    executorService.shutdown();
}

输出结果

pool-1-thread-1gogogo
pool-1-thread-3gogogo
pool-1-thread-4gogogo
pool-1-thread-2gogogo
pool-1-thread-5gogogo
pool-1-thread-1正在操作
pool-1-thread-5正在操作
pool-1-thread-5释放操作
pool-1-thread-2正在操作
pool-1-thread-2释放操作
pool-1-thread-3正在操作
pool-1-thread-3释放操作
pool-1-thread-4正在操作
pool-1-thread-4释放操作
pool-1-thread-1释放操作

进行改造使用我们的并发工具Semaphore

private static Semaphore semaphore = new Semaphore(1,false);

public static void main(String[] args) {
    ExecutorService executorService = Executors.newFixedThreadPool(5);
    IntStream.range(0,5).forEach(i -> executorService.submit(() ->{
        try {
            semaphore.acquire();
            System.out.println(Thread.currentThread().getName() + "gogogo");
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + "正在操作");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        semaphore.release();
        System.out.println(Thread.currentThread().getName() + "释放操作");

    }));

    executorService.shutdown();
}

输出结果

pool-1-thread-1gogogo
pool-1-thread-1正在操作
pool-1-thread-1释放操作
pool-1-thread-2gogogo
pool-1-thread-2正在操作
pool-1-thread-2释放操作
pool-1-thread-3gogogo
pool-1-thread-3正在操作
pool-1-thread-3释放操作
pool-1-thread-4gogogo
pool-1-thread-4正在操作
pool-1-thread-4释放操作
pool-1-thread-5gogogo
pool-1-thread-5正在操作
pool-1-thread-5释放操作

我们在Semaphore的初始化参数中,设置了允许并发线程数量为1,表示只允许1个线程通过,当线程拿到许可证的时候进行执行,线程完成之后进行许可证的归还,给下一个进来的线程使用,直到任务结束。

5. Semaphore的主要方法和核心参数

核心参数

构造方法

代码如下:
默认是非公平锁,只要传入并发线程数量

public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}

public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

核心方法

//从此信号量获取许可,先休眠,直到获得可用线程或者被中断
void acquire() throws InterruptedException

//中断继续
void acquireUninterruptibly()

//从此信号量获取设定线程数许可,先休眠,直到获得可用线程或者被中断
void acquire(int permits)

//尝试获取许可,如果能够获取成功则立即返回true,否则,则返回false
boolean tryAcquire()

//和上面一样,设置了等待最长时间
boolean tryAcquire(long timeout, TimeUnit unit)

//释放许可
void release()

//返回当前可用的许可证数
int availablePermits()

//等待许可证数
int getQueueLength()

//返回正在等待线程的合集
Collection<Thread> getQueuedThreads()

6. Semaphore的原理

实现的核心还是AQS的共享模式

Sync extends AbstractQueuedSynchronizer

acquire()

//以共享模式获取,如果中断则中止.
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
            //1. 通过首先检查中断状态,中断返回异常
    if (Thread.interrupted())
        throw new InterruptedException();
        // 2. 以共享模式获取,获取到了锁,接下去,执行,没有就排队
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}

//非公平模式共享锁获取
protected int tryAcquireShared(int acquires) {
    for (;;) {
        //判断当前节点在同步队列中是否有前驱节点的判断,获取不到返回-1
        if (hasQueuedPredecessors())
            return -1;
        //Semaphore用AQS的state变量的值代表可用许可数    
        int available = getState();
        int remaining = available - acquires;
        //如果剩余许可数小于0或者CAS将当前可用许可数设置为剩余许可数成功,则返回成功许可数
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}

private void doAcquireSharedInterruptibly(int arg)
    throws InterruptedException {
        //加入等待队列
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        //自旋过程中的退出条件是是当前节点的前驱节点是头结点并且tryAcquireShared(arg)
        //返回值大于等于0即能成功获得同步状态
        for (;;) {
            //获取前驱节点
            final Node p = node.predecessor();
            if (p == head) {
                //争夺锁
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    //先把 head 给占了,然后唤醒队列中其他的线程
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        //失败的话,取消状态清除该节点
        if (failed)
            cancelAcquire(node);
    }
}

//设置head的值,完成初始化工作
private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

7. Semaphore需要注意的问题

release()不能随便调用,调用一次就增加一次

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 class SemaphoreTest {
    private static Semaphore semaphore = new Semaphore(1,false);

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        IntStream.range(0,5).forEach(i -> executorService.submit(() ->{
            try {
                System.out.println("after:" + semaphore.availablePermits());
                //semaphore.acquire();
                System.out.println(Thread.currentThread().getName() + "gogogo");
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + "正在操作");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                semaphore.release();
                System.out.println("before:" + semaphore.availablePermits());
                System.out.println(Thread.currentThread().getName() + "释放操作");
}
            semaphore.release();
            System.out.println("before:" + semaphore.availablePermits());
            System.out.println(Thread.currentThread().getName() + "释放操作");

        }));

        executorService.shutdown();
    }
}
after:1
pool-1-thread-1gogogo
pool-1-thread-1正在操作
before:2
pool-1-thread-1释放操作
after:2
pool-1-thread-1gogogo
pool-1-thread-1正在操作
before:3
pool-1-thread-1释放操作
after:3
pool-1-thread-1gogogo
pool-1-thread-1正在操作
before:4
pool-1-thread-1释放操作

信号量泄露,指的是申请了但是没有释放,这会导致进入临界区的线程数量就会越来越少,随着时间的推移,最后许可证数量不够用,会导致线程卡死。

建议:在操作的时候,我们尽可能在finally中进行 semaphore.release() 的操作。

总结

以上就是关于信号量的全部内容,总体看来,用法比较简单,在实际的工作中需要对线程进行控制的场景,我们可以将他作为一个方案。

【Qt】Qt线程同步之QSemaphore信号量 Qt—线程同步之QSemaphore 文章目录Qt—线程同步之QSemaphore一、简介二、成员函数API(2-1)获取n个资源(2-2)释放n个资源(2-3)获取可用资源的数量(2-4)尝试获取n个资源(2-5)在一段时间内尝试获取n个资源三、使用示例(3-1)全局变量(3-2)Producer生产者类(3-3)Consumer消费者类(3-4)main函数 一、简介 ​ 信号量是互斥锁的泛化。一个互斥的对象只能被锁定一次,但是可以多次获取一个信号量。信号量通常用于:保护一定数量的相同资源。 二、成员 阅读详情

相关推荐

QT 线程之QSemaphore(深入理解)

QT多线程专栏共有14篇文章,从初识线程到、QMutex锁、QSemaphore信号量、Emit、Sgnals、Slot主线程子线程互相传值同步变量、QWaitCondition、事件循环、QObjects、线程安全、线程同步、线程异步、QThreadPool线程池等线程操作进行了全面!深入!的研究和详细的说明

断点 1万+

线程 | 并发工具的使用-Semaphore

线程-并发工具-Semaphore 文章目录线程-并发工具-Semaphore前言一、Semaphore是什么?二、主要方法和核心参数1.核心参数2.构造方法3.核心方法三、原理四、实践总结 前言 concurrent包为我们提供了多种防止多线程同时操作一个资源的方法。 volatile 原子类 Synchronized和Lock Semaphore 我们主要来学习下 Semaphore 一、Semaphore是什么? Semaphore被翻译为计数信号量,通常使用

小郭的博客 314

封神台靶场,为了小芳,冲之——cookie注入

第二个靶场 打开网站 找一下注入点 一般在新闻这种里面点开看看 先判断是否存在注入 有waf 尝试了很多方法绕过都没成功,/and/,aandnd,AnD等一系列都没绕过去,乌鸡鲅鱼 删掉id=169 发现 然后判断cookie注入 在搜索框输入 Javascript:alert(document.cookie=”id=”+escape(‘169’)); 然后弹窗了(其中 “id=” 取决URL中的id= escape中的数值也是URL中id=的值)返回刚刚的页面,把ID删除后一样能正常显示。说明服务器能

Wubuqing的博客 653

Java程序员要掌握什么 怎么防止出现多线程错误

Java程序员要掌握什么?怎么防止出现多线程错误​?Java是最先支持多线程的开发的语言之一,优秀的Java程序员一定要掌握多线程。使用多线程是为了提高性能,但如果多线程使用不当,不但性能提升不明显,而且会使得资源消耗更大。下面就给大家分享防止出现多线程错误的几个技巧。 1、避免全局状态 首先,牢记“全局状态”问题。如果你正创建一个多线程应用,那么应该密切关注任何可能全局...

qq_43444478的博客 363

手把手教你用CEFSharp打造防关联指纹浏览器(附完整代码)

本文详细介绍了如何基于CEFSharp框架,从零开始构建一个企业级的防关联指纹浏览器。通过剖析核心架构、多进程隔离实现、动态指纹混淆(如Canvas、WebGL)的实战代码,以及代理集成与性能优化,为跨境电商、广告投放等场景提供了一套可控、安全的开源解决方案。

fern8的博客 962

保姆级教程:在PX4 Gazebo仿真中为Iris无人机挂载Intel D435i深度相机(含完整配置流程)

本文提供在PX4 Gazebo仿真环境中为Iris无人机挂载Intel D435i深度相机的完整配置流程,涵盖环境准备、模型部署、插件安装及调试技巧。通过详细步骤和常见问题解决方案,帮助开发者快速搭建深度相机仿真环境,适用于无人机视觉开发与算法测试。

weixin_28688385的博客 337

【JUC基础】08. 三大工具类

JUC包中包含了三个非常实用的工具类:CountDownLatch(倒计数器),CyclicBarrier(循环栅栏),Semaphore(信号量)。

有一只柴犬的专栏 1523

Understanding Lockup Cells

工具会分析扫描链和EDT逻辑之间的控制时序元素的时钟的时序关系,当必须要同步时钟并保持数据完整性时插入边沿触发寄存器(lockup cells)。可以使用report_edt_lockup_cells命令来展示工具已经插入的lockup cells的详细报告。

weixin_44954910的博客 1080

Java高并发理论到实践--JUC之Semaphore

由于最近的项目需求:要求限制对OpenMLDB的并发访问量。 当然也看到面试题问:有三个线程T1,T2,T3,如何保证顺序执行? 以及之前去华为OD面试:三个线程交替打印ABC如何实现? 这些问题我都采用Java并发工具Semaphore(信号量)来实现的,所以记录一下这个知识点。

qq_40623672的博客 1927

基于YOLOv8的铁路轨道异物检测系统设计与实现

目标检测是计算机视觉领域的核心任务之一,旨在识别图像中特定目标的位置和类别。传统方法主要依赖手工特征(如HOG、SIFT等)和分类器(如SVM),而深度学习方法通过卷积神经网络自动学习特征表示,显著提升了检测性能。YOLO(You Only Look Once)系列算法作为单阶段目标检测的代表,以其高效的检测速度著称。YOLOv8是Ultralytics公司于2023年发布的最新版本,在精度和速度上都有进一步提升。

YOLO 574

QGis二次开发 —— 5.2、基于qgis搭建离线地图服务并开发雷达扫描、无人机飞行跟踪等(附:源码及工程)

QGis二次开发 —— 5.2、基于qgis搭建离线地图服务并开发雷达扫描、无人机飞行跟踪等(附:源码及工程)

224

JUC系列——Semaphore

简介 Semaphore是一种在多线程环境下负责协调各个线程,以保证它们能够正确、合理的使用公共资源。 Semaphore是一种计数信号量,用于管理一组资源,内部是基于AQS的共享模式。它相当于控制使用公共资源的活动线程的数量。 主要方法 主要方法: Semaphore(int permits):构造方法,创建具有给定许可数的计数信号量并设置为非公平信号量。 Semaphore(int perm...

耘田 1750

YOLOv9原创自研:卷积魔改 | 一种新颖的风车形卷积(PConv)融合注意力机制,加强微弱小目标检测能力

为了进一步提升小目标检测能力,风车形卷积(PConv)融合注意力机制进一步提升小目标检测能力

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 362

Semaphore详解

Java并发包中的Semaphore是实现资源访问控制的核心工具,通过虚拟许可机制限制线程并发数。其核心特性包括:基于AQS实现线程安全控制、支持公平/非公平两种获取模式、提供超时和中断机制。典型应用场景包括数据库连接池管理、API限流和并发任务调度。最佳实践建议:动态配置许可数量、使用try-with-resources模式避免许可泄漏、实现分级超时控制。需注意解决许可泄漏问题,确保资源释放。Semaphore为JUC包中处理资源竞争问题提供了高效解决方案。

csdn_tom_168的博客 980

Python-自动化绘制股票价格通道线

创建更强大、更可靠的价格通道检测算法。

探索人工智能领域 948

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

JUC详解

JUC是java.util.concurrent包,JUC是包的简称,Java并发工具包,提供了各种各样的控制同步和线程通信的工具类;并发的本质是:充分利用CPU资源;

weixin_44273367的博客 2217

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Java8中JUC包同步工具类深度解析(Semaphore,CountDownLatch,CyclicBarrier,Phaser)

工具类主要用途内部原理使用场景Semaphore控制访问某个或多个共享资源的线程数量基于AQS实现,维护一个许可计数器需要限制并发访问共享资源的场景,如连接池、线程池等允许一个或多个线程等待其他线程完成操作基于AQS实现,维护一个计数器用于协调一组线程的执行顺序,例如启动多个线程并行处理任务,并在所有任务完成后执行汇总操作让一组线程互相等待,直到所有线程都到达某个公共屏障点使用锁和条件变量实现,维护屏障的周期和计数器。

1688

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Java并发包(JUC)中的信号量Semaphore详解,深入浅出Semaphore

🍋🔥 🍑🚀支持博主:点赞👍、收藏⭐、留言💬 🐾 目录1.🍅Semaphore简介1.1🍑Semaphore是什么1.2🍑Semaphore的作用2.🍅Semaphore中的方法(我们在之后的章节中会详细讲解,读者可以先大致看一下)3.🍅acquire()方法和release()方法3.1🍑构造方法、acquire()方法和release()方法简介3.2🍑Semaphore构造器中控制线程并发量为一(某段时间内只能并发一个线程)3.3🍑Semaphore构造器中控制线程并发量为二(只能并发两个线程,即两个线

MrYushiwen的博客 2684

JUC之Semaphore

Semaphore的介绍

爪哇岛咖啡种植农场 221

JUC系列之《并发流程控制大师:Semaphore》

Semaphore是Java并发编程中管理资源访问的关键工具,通过"许可证"机制控制并发线程数量。它支持公平与非公平模式,适用于数据库连接池、限流等场景。核心方法包括acquire()获取许可和release()释放许可,使用时需确保在finally块中释放资源以防泄漏。相比CountDownLatch等工具,Semaphore更专注于资源访问控制,是构建高效并发系统的重要组件。掌握其原理有助于理解更复杂的分布式资源管理方案。

houzhangbo的专栏 279

Semaphore线程信号量

Semaphore 也是juc中的一个关键类,他与之前的lock 类似,也有公平和非公平两种,它与他们应用含义,引用场景有很大的不同;与阻塞队列类似,但是也不一样;听我细细道来~Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。它用来控制访问资源的最大线程数量;例如上厕所,只有十个坑位,所以最多只能允许十个人同时使用;

学海无涯,我用JAVA 1032

JUC高并发编程详解

首先,让我们认识一下JUC。JUC是Java 5引入的一个包,旨在提供更强大的并发编程支持。可以把它想象成一座丰富多彩的工具库,里面有各种各样的工具,帮助我们轻松应对并发的各种问题。JUC提供了丰富的工具和框架,帮助开发者更轻松地应对高并发编程的挑战。从线程池、原子操作、并发工具类到线程安全容器和异步编程,每个组件都为我们提供了强大的功能。通过深入理解并合理使用这些工具,我们能够编写出高效、安全且易维护的高并发程序。高并发编程可能是一项复杂的任务,但通过学习和实践,我们可以逐步掌握其中的要点。

繁依Fanyi的博客 1261

【javaEE】多线程--JUC的常见类

本文介绍了Java并发编程中的JUC工具包,重点讲解了Callable接口、ReentrantLock锁和Semaphore信号量的使用与特性。Callable通过FutureTask实现带返回值的线程任务,比Runnable更灵活。ReentrantLock提供了手动加锁、超时等待和公平锁等高级功能,与synchronized各有适用场景。Semaphore则通过计数器控制资源访问,支持PV原子操作。文章还总结了三种创建线程的方式,并对比了synchronized和ReentrantLock在实现机制、加

2301_81614213的博客 1053

【JUC系列-06】深入理解Semaphore底层原理和基本使用

深入理解Semapore的底层原理和基本使用

zhenghuishengq的博客 1885
上一篇: Redis之父实名认证“Redis实战文档”,你拥有了嘛?
下一篇: 宝典在手offer我有,Java面试宝典让你抓住金九银十的尾巴,逆风起飞
Java技术那些事儿
博客等级 码龄5年 2306粉丝 2584原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值