首页 > 编程语言 >[Java并发]Semaphore

[Java并发]Semaphore

时间:2024-08-27 22:52:56浏览次数:9  
标签:Java permits int 获取 信号量 并发 线程 Semaphore

Semaphore是一种同步辅助工具,翻译过来就是信号量,用来实现流量控制,它可以控制同一时间内对资源的访问次数.

无论是Synchroniezd还是ReentrantLock,一次都只允许一个线程访问一个资源,但是Semaphore可以指定多个线程同时访问某一个资源.

Semaphore有一个构造函数,可以传入一个int型整数n,表示某段代码最多只有n个线程可以访问,如果超出了n,那么请等待,等到某个线程执行完毕这段代码块,下一个线程再进入。

信号量上定义两种操作:

  • acquire(获取):当一个线程调用acquire操作时,它要么成功获取到信号量(信号量减1),要么一直等下去,直到有线程释放信号量,或超时,Semaphore内部会维护一个等待队列用于存储这些被暂停的线程.
  • release(释放)实际上会将信号量的值+1,然后唤醒相应Sepmaphore实例的等待队列中的一个任意等待线程.

信号量主要用于两个目的:

用于多个共享资源的互斥使用
用于并发线程数的控制

例子
以下的例子:5个线程抢3个车位,同时最多只有3个线程能抢到车位,等其他线程释放信号量后,才能抢到车位.

public static void main(String[] args) {
	Semaphore semaphore = new Semaphore(3);

	for (int i = 0; i < 5; i++) {
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					semaphore.acquire();//申请资源
					System.out.println(Thread.currentThread().getName()+"抢到车位");
					ThreadUtil.sleep(RandomUtil.randomInt(1000,5000));
					System.out.println(Thread.currentThread().getName()+"归还车位");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}finally {
					//释放资源
					semaphore.release();
				}

			}
		},"线程"+i).start();
	}
}

注意事项
Semaphore.acquire()和Semaphore.release()总是配对使用的,这点需要由应用代码自身保证.
Semaphore.release()调用应该放在finally块中,已避免应用代码出现异常的情况下,当前线程所获得的信号量无法返还.
如果Semaphore构造器中的参数permits值设置为1,所创建的Semaphore相当于一个互斥锁.与其他互斥锁不同的是,这种互斥锁允许一个线程释放另外一个线程所持有的锁.因为一个线程可以在未执行过Semaphore.acquire()的情况下执行相应的Semaphore.release().
默认情况下,Semaphore采用的是非公平性调度策略.
原理
abstract static class Sync extends AbstractQueuedSynchronizer {
//省略
}
Semaphore内部使用Sync类,Sync又是继承AbstractQueuedSynchronizer,所以Sync底层还是使用AQS实现的.Sync有两个实现类NonfairSync和FairSync,用来指定获取信号量时是否采用公平策略.

初始化方法
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}

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

Sync(int permits) {
setState(permits);
}
如上所示,Semaphore默认采用非公平策略,如果需要使用公平策略则可以使用带两个参数的构造函数来构造Semaphore对象。

参数permits被传递给AQS的state值,用来表示当前持有的信号量个数.

void acquire()方法
当前线程调用该方法的目的是希望获取一个信号量资源。

如果当前信号量个数大于0,则当前信号量的计数会减1,然后该方法直接返回。否则如果当前信号量个数等0,则当前线程会被放入AQS的阻塞队列。当其他线程调用了当前线程的interrupt()方法中断了当前线程时,则当前线程会抛出InterruptedException异常返回。

//Semaphore方法
public void acquire() throws InterruptedException {
//传递参数为1,说明要获取1个信号量资源
sync.acquireSharedInterruptibly(1);
}

//AQS的方法
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
//(1)如果线程被中断,则抛出中断异常
if (Thread.interrupted())
throw new InterruptedException();
//(2)否则调用Sync子类方法尝试获取,这里根据构造函数确定使用公平策略
if (tryAcquireShared(arg) < 0)
//如果获取失败则放入阻塞队列.然后再次尝试,如果使用则调用park方法挂起当前线程
doAcquireSharedInterruptibly(arg);
}
由如上代码可知,acquire()在内部调用了Sync的acquireSharedlnterruptibly方法,后者会对中断进行响应(如果当前线程被中断,则抛出中断异常)。尝试获取信号量资源的AQS的方法 tryAcquireShared是由Sync的子类实现的,所以这里分别从两 方面来讨论。

先讨论非公平策略NonfairSync类的tryAcquireShared方法,代码如下:

protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}

final int nonfairTryAcquireShared(int acquires) {
for (;

标签:Java,permits,int,获取,信号量,并发,线程,Semaphore
From: https://www.cnblogs.com/DCFV/p/18383699

相关文章

  • Java元注解介绍
    Java四种元注解相关介绍概述注解从Java1.5引入以来,不断地简化我们编写代码的流程,逐渐的也成为了我们必学的一项技术。我们学习了各种注解,学习了他们的用法,学习了他们的限制,是否想过他们的组成呢,下面我将我对元注解的理解分享给大家。元注解是用来修饰注解的注解,在java.lang.ann......
  • Java数据结构栏目总结
     目录数组与稀疏数组队列:自己用数组模拟Queue环形队列,取模【取余】实现.单链表(LinkList)双向链表(Next、Pre)单向环形链表线性结构数组与稀疏数组稀疏数组,很多0值,可用于压缩特点:共n+1行3列,n为不同值的个数(0除外)第一行:数组的行数、列数、不同值的个数第二行:行......
  • 大厂员工,手把手教你开发一个高并发、高可用的营销活动
    前言这几年工作中做过不少营销活动,无论是电商业务、支付业务、还是信贷业务,营销在整个业务发展过程中都是必不可少的。如果前期营销宣传到位,会给业务带来一波不小的流量。那么作为技术,如何接住这波流量,而不是服务被打挂。今天大厂员工,手把手教你开发出一个高并发、高可用的营销活......
  • TCP并发服务器多线程和多进程方式以及几种IO模型
    1.阻塞I/O(BlockingI/O)在阻塞I/O模型中,当应用程序发起I/O操作时,整个进程会被阻塞,直到操作完成。在这个过程中,应用程序无法执行其他任务,必须等待I/O操作的完成。特点:简单性:编程简单,逻辑清晰,容易理解和实现。低效性:在高并发场景下,由于每个I/O操作都会阻塞整个进程,资......
  • Java毕设项目II基于Spring Boot的医药管理系统的设计与实现
    目录一、前言二、技术介绍三、系统实现四、论文参考五、核心代码六、源码获取全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末一、前言"在当今医疗信息化快速发展的背景下,设计......
  • 7.java面向对象
    面向过程&面向对象面向过程思想步骤清晰简单,第一步做什么,第二步做什么…...面对过程适合处理一些较为简单的问题。面向对象思想物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。......
  • Java 使用QQ邮箱的接收&发送功能,入门级教程
    进入qq邮箱主页面,点击账号下滑找到POP3...如果没有开启,需要开启,开启后,点击管理服务然后点击生成授权码按照步骤执行完成后,会给你需要的授权码1.拿到授权码后,导入相关依赖,和yml相关配置,注意修改配置文件的信息<dependency><groupId>org.apache.commons</......
  • Java10 集合
    集合集合集合接口等级:Collection:单例集合接口,将数据一个一个存储,存储的是值。ArrayList类:泛型集合Linkedlist集合:Vector集合:Stack集合:Vetor的子类Set接口:存储是无序的,且集合中的元素不可重复Hashset集合:Linkedhashset:是有序不重复的set集合,继承于hashsetTreeset:排序,去......
  • JAVA基础之二-面向对象简述
    java基础之二-面向对象简述一、概述如果有机会多接触几种语言,对于程序员多少是有好处的,至少有助于理解代码的运行真谛。高级语言有很多是面向对象的,因为面向对象的优点是显而易见的。这里比较知名的有rust,java,c++,c#但也有很多语言是面向过程的,鼎鼎有名有C,还有现在大家不......
  • 《JavaEE进阶》----1.<JavaEE进阶可以学到什么>
    本篇博客会讲到一、JavaEE进阶学习内容:1.框架的学习:Spring、SpringBoot、SpringMVC、MyBatis2.大项目实践3.源码阅读二、JavaEE简介B/S架构web开发流程web前端开发(了解)web后端开发(重点)三、什么是框架四、学习编程思维方式(重点:学习建议)学完JavaEE你的收获会持续......