(八)java多线程之Semaphore

news/2024/12/13 12:54:12

本人邮箱: <kco1989@qq.com>
欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco
github: https://github.com/kco1989/kco
代码已经全部托管github有需要的同学自行下载

引言

这节课,我们就开始讲一下信号量Semaphore

理论

Semaphore:一个可计数的信号量。一般,一个semaphore 信号量是一组许可证。如果必要,那个每次acquire获取许可都是阻塞的,直接一个许可证是可用的,并获取到。每次release释放,都会增加一个许可证,潜在的,也会释放一个阻塞请求。然而。并非每次许可对象都可以被使用的,这个Semaphore信号量只保存几个可用的许可证和相应的操作。

如果有几个线程数要访问几个共享资源的话,那么这时候就应该使用信号量。举例说明:这个有类Pool类,它就使用信号量在控制多线程去访问那么几个有限items

class Pool {private static final int MAX_AVAILABLE = 100;private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);public Object getItem() throws InterruptedException {available.acquire();return getNextAvailableItem();}public void putItem(Object x) {if (markAsUnused(x))available.release();}// Not a particularly efficient data structure; just for demoprotected Object[] items = ... whatever kinds of items being managedprotected boolean[] used = new boolean[MAX_AVAILABLE];protected synchronized Object getNextAvailableItem() {for (int i = 0; i < MAX_AVAILABLE; ++i) {if (!used[i]) {used[i] = true;return items[i];}}return null; // not reached}protected synchronized boolean markAsUnused(Object item) {for (int i = 0; i < MAX_AVAILABLE; ++i) {if (item == items[i]) {if (used[i]) {used[i] = false;return true;} elsereturn false;}}return false;}
}

在每个线程获取item之前,必须先从信号量获取许可。保证这个item对用户来说是可以使用的。当线程结束使用item时,并让item返回item池,这信号量会释放这个许可,之后允许使用线程可以获取到这个item。必须要注意的,在程序中,在获取许可和释放许可的死胡同并没有使用同步锁,信号量封装了限制对池的访问所需的同步,与维护池本身的一致性所需的任何同步。

  • Semaphore(int permits): 创建一个指定数量的许可的信号量

  • Semaphore(int permits, boolean fair) 创建一个指定数量的许可,并保证每个线程都是公平的,当fairtrue时,信号量会安装先进先出的原则来获取许可.

  • acquire() 在当前信号量中获取一个许可.当前线程会一直阻塞直到有一个可用的许可,或被其他线程中断.

  • acquireUninterruptibly(): 在当前信号量中获取一个许可.当前线程会一直阻塞直到有一个可用的许可.

  • tryAcquire() 在当前信号量尝试获取一个许可,如果有可用,则获取到这个许可,并立即返回true,后缀立即返回false

  • tryAcquire 在当前信号量获取一个许可,当前线程会一直阻塞直到有一个可用的许可.或指定时间超时了,或被其他线程中断.

  • release() 释放一个许可,把让它返回到这个信号量中.

  • acquire(int permits) 请求指定数量的许可,如果有足够的许可可用,那么当前线程会立刻返回,如果许可不足,则当前会一直等待,直到被其他线程中断,或获取到足够的许可.

  • acquireUninterruptibly(int permits) 请求指定数量的许可,如果有足够的许可可用,那么当前线程会立刻返回,如果许可不足,则当前会一直等待,直到获取到足够的许可.

  • tryAcquire(int permits) 在当前信号量尝试获取指定数量的许可,如果有可用,则获取到这个许可,并立即返回true,后缀立即返回false

  • tryAcquire(int permits, long timeout, TimeUnit unit) 在指定的超时时间,当前信号量尝试获取指定数量的许可,如果有可用,则获取到这个许可,并立即返回true,后缀立即返回false

  • release(int permits) 释放指定数量的许可

  • availablePermits() 返回当前信号量还有几个可用的许可

  • drainPermits() 请求并立即返回当前信号量可用的全部许可

  • reducePermits(int reduction) 根据指定的缩减量减小可用许可的数目。此方法在使用信号量来跟踪那些变为不可用资源的子类中很有用。此方法不同于 acquire,在许可变为可用的过程中,它不会阻塞等待。

  • isFair() 返回当前的信号量时候是公平的

  • hasQueuedThreads() 查询是否有线程正在等待获取。注意,因为同时可能发生取消,所以返回 true 并不保证有其他线程等待获取许可。此方法主要用于监视系统状态。

  • getQueueLength() 返回正在等待获取的线程的估计数目。该值仅是估计的数字,因为在此方法遍历内部数据结构的同时,线程的数目可能动态地变化。此方法用于监视系统状态,不用于同步控制。

  • getQueuedThreads() 返回一个 collection,包含可能等待获取的线程。因为在构造此结果的同时实际的线程 set 可能动态地变化,所以返回的 collection 仅是尽力的估计值。所返回 collection 中的元素没有特定的顺序。此方法用于加快子类的构造速度,提供更多的监视设施。

例子

看了前面那么方法的介绍,恐怕你想吐的的心都有了吧?还是让我们回归轻松愉快的例子来吧.这里我们还是继续举小明小红谈人生和理想的例子.之前他们在卧室里谈了好几百毫秒的人生和理想.顿时都感觉身疲惫,感觉身体好像被掏空了一样.所以这里他们都想洗一个热水澡,但是沐浴室只有三间,那就抢吧..ok,开始编程...

  • 首先,先编写一个沐浴室ShowerRoom

public class ShowerRoom {private static final int MAX_SIZE = 3;Semaphore semaphore = new Semaphore(MAX_SIZE);public void bathe(String name){try {semaphore.acquire();System.out.println(Thread.currentThread().getName() + " 洗唰唰啊..洗唰唰... ");Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}finally {System.out.println(Thread.currentThread().getName() + " 终于洗完澡了...");semaphore.release();try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}
}
  • 然后编写让小明小红去洗澡操作 BoyAndGril

public class BoyAndGril implements Runnable{ShowerRoom showerRoom;public BoyAndGril(ShowerRoom showerRoom) {this.showerRoom = showerRoom;}@Overridepublic void run() {String name = Thread.currentThread().getName();showerRoom.bathe(name);}
}
  • 最后,测试一下

public class TestMain {public static void main(String[] args) {Set<Thread> boyAndGril = new HashSet<>();ShowerRoom showerRoom = new ShowerRoom();for (int i = 0; i < 10; i ++){boyAndGril.add(new Thread(new BoyAndGril(showerRoom), "小明" + i + "号"));}for (int i = 0; i < 10; i ++){boyAndGril.add(new Thread(new BoyAndGril(showerRoom), "小红" + i + "号"));}for (Thread thread : boyAndGril){thread.start();}}
}

运行一下结果

小红3号 洗唰唰啊..洗唰唰... 
小红6号 洗唰唰啊..洗唰唰... 
小明0号 洗唰唰啊..洗唰唰... 
小红3号 终于洗完澡了...
小红2号 洗唰唰啊..洗唰唰... 
小红6号 终于洗完澡了...
小明2号 洗唰唰啊..洗唰唰... 
小明0号 终于洗完澡了...
小红1号 洗唰唰啊..洗唰唰... 
小红2号 终于洗完澡了...
小明5号 洗唰唰啊..洗唰唰... 
小明2号 终于洗完澡了...
小明7号 洗唰唰啊..洗唰唰... 
小红1号 终于洗完澡了...
小红0号 洗唰唰啊..洗唰唰... 
小明5号 终于洗完澡了...
小明7号 终于洗完澡了...
小明4号 洗唰唰啊..洗唰唰... 
小红4号 洗唰唰啊..洗唰唰... 
小红0号 终于洗完澡了...
小明3号 洗唰唰啊..洗唰唰... 
小明4号 终于洗完澡了...
小明9号 洗唰唰啊..洗唰唰... 
小红4号 终于洗完澡了...
小红7号 洗唰唰啊..洗唰唰... 
小明3号 终于洗完澡了...
小红5号 洗唰唰啊..洗唰唰... 
小红5号 终于洗完澡了...
小红9号 洗唰唰啊..洗唰唰... 
小红7号 终于洗完澡了...
小明6号 洗唰唰啊..洗唰唰... 
小明9号 终于洗完澡了...
小明1号 洗唰唰啊..洗唰唰... 
小红9号 终于洗完澡了...
小红8号 洗唰唰啊..洗唰唰... 
小明1号 终于洗完澡了...
小明8号 洗唰唰啊..洗唰唰... 
小明6号 终于洗完澡了...
小红8号 终于洗完澡了...
小明8号 终于洗完澡了...

ok,运行正常,程序中不会发生四个人以及四个以上的人在同时洗澡的情况.

如果有人觉得这个好像也没有使用什么共享资源啊,没有上面那个例子的item pool,那行,那把有关semaphore的代码注释掉,再运行一下.

小红3号 洗唰唰啊..洗唰唰... 
小红6号 洗唰唰啊..洗唰唰... 
小明0号 洗唰唰啊..洗唰唰... 
小红2号 洗唰唰啊..洗唰唰... 
小明2号 洗唰唰啊..洗唰唰... 
小红1号 洗唰唰啊..洗唰唰... 
小明5号 洗唰唰啊..洗唰唰... 
小明7号 洗唰唰啊..洗唰唰... 
小红0号 洗唰唰啊..洗唰唰... 
小明4号 洗唰唰啊..洗唰唰... 
小红4号 洗唰唰啊..洗唰唰... 
小明3号 洗唰唰啊..洗唰唰... 
小明9号 洗唰唰啊..洗唰唰... 
小红7号 洗唰唰啊..洗唰唰... 
小红5号 洗唰唰啊..洗唰唰... 
小红9号 洗唰唰啊..洗唰唰... 
小明6号 洗唰唰啊..洗唰唰... 
小明1号 洗唰唰啊..洗唰唰... 
小红8号 洗唰唰啊..洗唰唰... 
小明8号 洗唰唰啊..洗唰唰... 
小红3号 终于洗完澡了...
小红2号 终于洗完澡了...
小明2号 终于洗完澡了...
小红6号 终于洗完澡了...
小明0号 终于洗完澡了...
小明5号 终于洗完澡了...
小红0号 终于洗完澡了...
小明7号 终于洗完澡了...
小红1号 终于洗完澡了...
小明4号 终于洗完澡了...
小红4号 终于洗完澡了...
小明3号 终于洗完澡了...
小明9号 终于洗完澡了...
小红8号 终于洗完澡了...
小红5号 终于洗完澡了...
小红9号 终于洗完澡了...
小明6号 终于洗完澡了...
小明1号 终于洗完澡了...
小红7号 终于洗完澡了...
小明8号 终于洗完澡了...

发现这几十个人都同时在三间沐浴室里洗澡,那么肯定有只是一间会出现两人或两人以上同时洗澡的情况.如果浴室够大,大家都没有意见,那还好.就是如果肥皂掉了,这个时候,小明就得考虑要不要弯腰去捡了....

打赏

如果觉得我的文章写的还过得去的话,有钱就捧个钱场,没钱给我捧个人场(帮我点赞或推荐一下)
微信打赏
支付宝打赏


https://dhexx.cn/news/show-4079982.html

相关文章

python 编程spark_《Spark数据分析:基于Python语言 》 —1.3.3 Python函数式编程基础...

1.3.3 Python函数式编程基础Python的函数式编程包括函数式编程范式中你所期待的全部特性&#xff0c;如下所列&#xff1a;函数是一等公民&#xff0c;是编程的基本单元。函数都是给定一个输入就有一个输出&#xff0c;且输出仅取决于输入的纯函数(不允许使用会产生副作用的语…

业务工作流平台设计(二)

的确正如别人所说的那样&#xff0c;WF只是一个foundation而不是一个platform&#xff0c;如果要实现完整的业务工作流平台支持&#xff0c;就这个平台的实现来讲&#xff0c;从技术上说不是很难&#xff0c;我想微软应该可以实现这一点&#xff0c;也许是下一个版本&#xff1…

win7系统mysql连接不上数据库吗_win7安装2个mysql版本,连接不上数据库

提示1045 access denied for user rootlocalhost using password yes最后在 上找到了答案参考链接&#xff1a;http://bbs..net/topics/310006640解决方案&#xff1a;1. 开始 --> cmd --> net stop mysql (停用MySQL服务 没启动的可以省略)2. 找到安装路径 MySQL Se…

信息检索领域相关资料 zz

信息检索领域相关资料 zz zz from http://net.pku.edu.cn/~webg/IR-Guide.txt 信息检索领域相关资料 (A Guide to Information Retrieval)Organized by Hongfei YanLast updated on April 19, 2006---------------------ContentsBooks Finding Out About: Search Engine Techno…

探讨一下在Firefox中运行IE

一些站点仅在IE中才能够正常显示&#xff0c;包括微软的Windows更新站点。这时候&#xff0c;你可以使用免费的IE Tab扩展插件来让IE在Firefox中运行。当你查看一个需要IE才能正常显示的页面时&#xff0c;这个扩展就会启用微软的渲染引擎&#xff0c;让你毫无牵绊地浏览这个页…

springboot mysql多数据库_springboot多数据源(oracle、mysql)

1、application.properties配置&#xff1a;server.port8085server.tomcat.uri-encodingutf-8#MySQLspring.datasource.primary.driver-class-namecom.mysql.jdbc.Driverspring.datasource.primary.jdbc-urljdbc:mysql://localhost:3306/test_spboot?characterEncodingutf8spr…

vSphere6总提示已弃用VMFS卷

给几台ESXi6主机挂载了新的存储&#xff0c;结果主机全部变叹号&#xff0c;点进去看&#xff0c;提示下面的错误&#xff1a;Deprecated VMFS volume(s) found on the host. Please consider upgrading volume(s) to the latest version.即“已弃用VMFS卷”。这让我很奇怪&…

我是幸运博友:赴厦门参加微软会议(准备篇)

我在51cto写blog很久了&#xff0c;也是51cto博客交流群(14541432)里的老人了&#xff0c;相信在群的朋友都知道“雨寒”吧&#xff0c;呵呵&#xff0c;千万别嫌哥们在群里太活跃&#xff0c;扰了大家的工作啊。我确实是个活跃的人&#xff0c;呵呵&#xff0c;要不&#xff0…

三类完整性规则mysql下规则实现短语_《数据库原理》一到三章节简答题总结(一)...

希赛软考学院整理《数据库原理》第一章到第三章简答题及答案&#xff0c;希望对参加数据库系统工程师考试的你有所帮助&#xff01;第一章 数据库概论1. 人工管理阶段数据管理的特点&#xff1a;(1)数据不保存在机器中(2)无专用的软件对数据进行管理(3)只有程序的概念&#xff…

软件开发同行请注意

软件开发同行请注意::新的一年要有新的目标,为提高全体开发人的生活水平,为充分显示开发人的生存价值,逐步过上正常人的作息时间.请一起提高制作费用!特别是做管理软件的,报价应该2000-10000元.给别人打工的设计者,月工资要求涨到4000-8000元.现物价都上涨,就是开发费用严重滑坡…