本人邮箱: <kco1989@qq.com>
欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco
github: https://github.com/kco1989/kco
代码已经所有托管github有须要的同窗自行下载java
这节课,咱们就开始讲一下信号量Semaphore
git
Semaphore
:一个可计数的信号量。通常,一个semaphore
信号量是一组许可证。若是必要,那个每次acquire
获取许可都是阻塞的,直接一个许可证是可用的,并获取到。每次release
释放,都会增长一个许可证,潜在的,也会释放一个阻塞请求。然而。并不是每次许可对象均可以被使用的,这个Semaphore
信号量只保存几个可用的许可证和相应的操做。github
若是有几个线程数要访问几个共享资源的话,那么这时候就应该使用信号量。举例说明:这个有类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 demo protected Object[] items = ... whatever kinds of items being managed protected 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; } else return false; } } return false; } }
在每一个线程获取
item
以前,必须先从信号量获取许可。保证这个item
对用户来讲是可使用的。当线程结束使用item
时,并让item
返回item池,这信号量会释放这个许可,以后容许使用线程能够获取到这个item
。必需要注意的,在程序中,在获取许可和释放许可的死胡同并无使用同步锁,信号量封装了限制对池的访问所需的同步,与维护池自己的一致性所需的任何同步。微信
Semaphore(int permits)
: 建立一个指定数量的许可的信号量数据结构
Semaphore(int permits, boolean fair)
建立一个指定数量的许可,并保证每一个线程都是公平的,当fair
为true
时,信号量会安装先进先出的原则来获取许可.多线程
acquire()
在当前信号量中获取一个许可.当前线程会一直阻塞直到有一个可用的许可,或被其余线程中断.ide
acquireUninterruptibly()
: 在当前信号量中获取一个许可.当前线程会一直阻塞直到有一个可用的许可.测试
tryAcquire()
在当前信号量尝试获取一个许可,若是有可用,则获取到这个许可,并当即返回true
,后缀当即返回false
ui
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; } @Override public 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号 终于洗完澡了...
发现这几十我的都同时在三间沐浴室里洗澡,那么确定有只是一间会出现两人或两人以上同时洗澡的状况.若是浴室够大,你们都没有意见,那还好.就是若是肥皂掉了,这个时候,小明
就得考虑要不要弯腰去捡了....
若是以为个人文章写的还过得去的话,有钱就捧个钱场,没钱给我捧我的场(帮我点赞或推荐一下)