本文共 3240 字,大约阅读时间需要 10 分钟。
在介绍Semaphore信号量的时候我们要知道以往学习的synchronized和ReentrantLock锁都是只允许在同一时刻只能有一个线程去访问资源。这与我们生活中的一些情况是不符合的。而Semaphore信号量可以控制多个线程去访问特定的资源。
Semaphore的使用场景:
理解:假设有一个停车场,一次可以停车5辆。而停车管理员就好比我们的Semaphore他可以控制需要停车的车辆。每一个停车位都有一把锁,当来了一辆车后就给该车主一把钥匙,在停车结束后就车主将车钥匙归还给管理员。如果管理员此时已经没有钥匙了就表明停车场没有多余的停车位,那么再来停车的车主则需要等待。
相信这样理解Semaphore就会简单一些。那么我们看一下这个Semaphore是怎么使用的吧
构造方法:Semaphore(int permits) //好比我们停车场的车位数 创建具有给定的许可数和非公平的公平设置的 Semaphore。 Semaphore(int permits, boolean fair)//是否公平表明在等待的时候是否按照车主的先后循序 创建具有给定的许可数和给定的公平设置的 Semaphore。
常用的方法(大白话)
acquire() 从此信号量中获取一个许可证,获取许可证之前处于阻塞状态。可响应中断acquire(int permit) 从此信号量中获取permit个许可证。好比大车需要两个停车位acquireUninterruptibly(int permits) 和上述情况相似,只是不会响应中断标记//带有try的我们可以和ReentrantLock中的tryLock等方法进行比较tryAcquire() 尝试获取许可证,会立即返回。获取返回true反之返回falsetryAcquire(int permit) 尝试获取permit个许可证,也会立即返回tryAcquire(long timeout, TimeUnit unit) 在指定时间内获取许可f证,在指定时间内获取的话返回true,反之返回false 会响应中断release() 释放许可证,就是车主停车结束后需要把锁交给管理员release(int permit) 释放permit个许可证vailablePermits() 当前可用的许可证
代码模拟:一个可以同时停5辆车的停车位一次来了二十辆车。每次停车随机秒
import java.util.Random;import java.util.concurrent.Semaphore;public class SemaphoreTest { //定义一个方法用来表示停车的随机时间 public static int getTime(){ return new Random().nextInt(3000)+1000; } public static void main(String[] args) { Semaphore semaphore = new Semaphore(5); //模拟20辆车进行停车 for (int i = 1; i <= 20; i++) { new ThreadA("车辆"+i,semaphore).start(); } }}class ThreadA extends Thread{ Semaphore semaphore; public ThreadA (String name,Semaphore semaphore){ super(name); this.semaphore = semaphore; } @Override public void run() { boolean falg = false; try{ //尝试获取许可证,即尝试是否可以停车,不能停车的话处于阻塞状态 semaphore.acquire(); falg = true; System.out.println(Thread.currentThread().getName()+"停车成功"+"==可用停车位为"+semaphore.availablePermits()); Thread.sleep(SemaphoreTest.getTime()); }catch (Exception e){ e.printStackTrace(); }finally { if(falg){ //停车结束释放许可证 semaphore.release(); System.out.println(Thread.currentThread().getName()+"停车结束"+"==可用停车位为"+semaphore.availablePermits()); } } }}…
上述结果就是模拟我们平时停车。
代码中值得注意的地方
1.在什么时候释放许可证?
大家可能会说释放许可证肯定是在finally代码块中执行的啊。其实这是很不严谨的,我们知道有可能在获取锁的过程如果出现异常那么将会导致没有获取许可证但是却释放了许可证。将会导致许可证的数量增加。
解决方案就是我们在获取许可证的时候设置一个变量,在finally块中对变量进进行判断是否需要释放许可证。
boolean falg = false;try{ semaphore.acquire(); falg = true;//如果获取许可证成功,那么将会设置变量为true}catch(Exception e){ ....}finally{ if(falg){ //只有该变量设置为true,才表明获取了许可证 semaphore.release();//释放许可证}}
2.如果获取许可证后不释放会怎么样
获取许可证后如果不释放将会导致需要许可证(无设置延时策略)的线程阻塞。好比停车后将锁带走将导致后边的车辆不能使用该停车位。
在规定的时间内获取许可证
现实生活中我们去停车时被告知需要等待,当我们得知需要等待的时间过长时我们的一般做法就是换一家停车场。而线程也可以进行此操作
上述代码进行修改:
如果在2秒后获取不到许可证将会选择直接返回boolean b = semaphore.tryAcquire(2, TimeUnit.SECONDS); if (!b) { System.out.println(Thread.currentThread().getName()+"等待时间太久了,更换停车场"); return; }
结果:部分截图
我们看到有的车辆发现需要等待时间较长时选择了不在这里停车补充:
1.Semaphore默认是不公平的信号量
公平更接近与现实生活,但是效率很低2.方法声明InterruptException异常的表明可以响应中断
如果调用线程的interrupt方法时所有声明异常的方法就会抛出异常,并将中断标记清除。转载地址:http://ejxzi.baihongyu.com/