多线程与并发----Semaphere同步 矫情吗;* 2021-09-15 10:20 269阅读 0赞 **Semaphore实现信号灯:** **Semaphore可以维护当前访问自身的线程个数,并且提供了同步机制。**使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。 **java.util.concurrent.Semaphore** 一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。 Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。例如,下面的类使用信号量控制对内容池的访问: > 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; > } > } > > > **获得一项前,每个线程必须从信号量获取许可,从**而保证可以使用该项。该线程结束后,将项返回到池中并将许可返回到该信号量,从而允许其他线程获取该项。 **注意,调用** **acquire()** ** 时无法保持同步锁,因为这会阻止将项返回到池中。** 信号量封装所需的同步,以限制对池的访问,这同维持该池本身一致性所需的同步是分开的。 > <table> > <tbody> > <tr> > <td colspan="5" style="background:rgb(204,204,255);"><p>构造方法摘要</p></td> > </tr> > <tr> > <td colspan="5" style="background:rgb(255,255,255);"><p><a rel="nofollow">Semaphore</a>(int permits) <span style="font-family:'宋体';">创建具有给定的许可数和非公平的公平设置的 </span><span style="font-family:'Times New Roman';">Semaphore</span><span style="font-family:'宋体';">。</span></p></td> > </tr> > <tr> > <td colspan="5"><p><a rel="nofollow">Semaphore</a>(int permits, boolean fair) <span style="font-family:'宋体';">创建具有给定的许可数和给定的公平设置的 </span><span style="font-family:'Times New Roman';">Semaphore</span><span style="font-family:'宋体';">。</span></p></td> > </tr> > <tr> > <td colspan="5" style="background:rgb(204,204,255);"><p>方法摘要</p></td> > </tr> > <tr> > <td><p> void</p></td> > <td colspan="4"><p><a rel="nofollow">acquire</a>() <span style="font-family:'宋体';">从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被</span><a rel="nofollow">中断</a>。</p></td> > </tr> > <tr> > <td style="background:rgb(255,255,255);"><p> void</p></td> > <td colspan="4" style="background:rgb(255,255,255);"><p><a rel="nofollow">acquire</a>(int permits) <span style="font-family:'宋体';">从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞,或者线程已被</span><a rel="nofollow">中断</a>。</p></td> > </tr> > <tr> > <td><p> void</p></td> > <td colspan="4"><p><a rel="nofollow">acquireUninterruptibly</a>() <span style="font-family:'宋体';">从此信号量中获取许可,在有可用的许可前将其阻塞。</span></p></td> > </tr> > <tr> > <td style="background:rgb(255,255,255);"><p> void</p></td> > <td colspan="4" style="background:rgb(255,255,255);"><p><a rel="nofollow">acquireUninterruptibly</a>(int permits) <span style="font-family:'宋体';">从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞。</span></p></td> > </tr> > <tr> > <td><p> int</p></td> > <td colspan="4"><p><a rel="nofollow">availablePermits</a>() <span style="font-family:'宋体';">返回此信号量中当前可用的许可数。</span></p></td> > </tr> > <tr> > <td style="background:rgb(255,255,255);"><p> int</p></td> > <td colspan="4" style="background:rgb(255,255,255);"><p><a rel="nofollow">drainPermits</a>() <span style="font-family:'宋体';">获取并返回立即可用的所有许可。</span></p></td> > </tr> > <tr> > <td colspan="4"><p>protected <a title="java.util 中的接口" rel="nofollow">Collection</a><<a title="java.lang 中的类" rel="nofollow">Thread</a>></p></td> > <td><p><a rel="nofollow">getQueuedThreads</a>() <span style="font-family:'宋体';">返回一个 </span><span style="font-family:'Times New Roman';">collection</span><span style="font-family:'宋体';">,包含可能等待获取的线程。</span></p></td> > </tr> > <tr> > <td colspan="3" style="background:rgb(255,255,255);"><p> int</p></td> > <td colspan="2" style="background:rgb(255,255,255);"><p><a rel="nofollow">getQueueLength</a>() <span style="font-family:'宋体';">返回正在等待获取的线程的估计数目。</span></p></td> > </tr> > <tr> > <td colspan="3"><p> boolean</p></td> > <td colspan="2"><p><a rel="nofollow">hasQueuedThreads</a>() <span style="font-family:'宋体';">查询是否有线程正在等待获取。</span></p></td> > </tr> > <tr> > <td colspan="3" style="background:rgb(255,255,255);"><p> boolean</p></td> > <td colspan="2" style="background:rgb(255,255,255);"><p><a rel="nofollow">isFair</a>() <span style="font-family:'宋体';">如果此信号量的公平设置为 </span><span style="font-family:'Times New Roman';">true</span><span style="font-family:'宋体';">,则返回 </span><span style="font-family:'Times New Roman';">true</span><span style="font-family:'宋体';">。</span></p></td> > </tr> > <tr> > <td colspan="3"><p>protected void</p></td> > <td colspan="2"><p><a rel="nofollow">reducePermits</a>(int reduction) <span style="font-family:'宋体';">根据指定的缩减量减小可用许可的数目。</span></p></td> > </tr> > <tr> > <td colspan="2" style="background:rgb(255,255,255);"><p> void</p></td> > <td colspan="3" style="background:rgb(255,255,255);"><p><a rel="nofollow">release</a>() <span style="font-family:'宋体';">释放一个许可,将其返回给信号量。</span></p></td> > </tr> > <tr> > <td colspan="2"><p> void</p></td> > <td colspan="3"><p><a rel="nofollow">release</a>(int permits) <span style="font-family:'宋体';">释放给定数目的许可,将其返回到信号量。</span></p></td> > </tr> > <tr> > <td colspan="2" style="background:rgb(255,255,255);"><p> <a title="java.lang 中的类" rel="nofollow">String</a></p></td> > <td colspan="3" style="background:rgb(255,255,255);"><p><a rel="nofollow">toString</a>() <span style="font-family:'宋体';">返回标识此信号量的字符串,以及信号量的状态。</span></p></td> > </tr> > <tr> > <td colspan="2"><p> boolean</p></td> > <td colspan="3"><p><a rel="nofollow">tryAcquire</a>() <span style="font-family:'宋体';">仅在调用时此信号量存在一个可用许可,才从信号量获取许可。</span></p></td> > </tr> > <tr> > <td colspan="2" style="background:rgb(255,255,255);"><p> boolean</p></td> > <td colspan="3" style="background:rgb(255,255,255);"><p><a rel="nofollow">tryAcquire</a>(int permits) <span style="font-family:'宋体';">仅在调用时此信号量中有给定数目的许可时,才从此信号量中获取这些许可。</span></p></td> > </tr> > <tr> > <td colspan="2"><p> boolean</p></td> > <td colspan="3"><p><a rel="nofollow">tryAcquire</a>(int permits, long timeout, <a title="java.util.concurrent 中的枚举" rel="nofollow">TimeUnit</a> unit) <span style="font-family:'宋体';">如果在给定的等待时间内此信号量有可用的所有许可,并且当前线程未被</span><a rel="nofollow">中断</a>,则从此信号量获取给定数目的许可。</p></td> > </tr> > <tr> > <td colspan="2" style="background:rgb(255,255,255);"><p> boolean</p></td> > <td colspan="3" style="background:rgb(255,255,255);"><p><a rel="nofollow">tryAcquire</a>(long timeout, <a title="java.util.concurrent 中的枚举" rel="nofollow">TimeUnit</a> unit) <span style="font-family:'宋体';">如果在给定的等待时间内,此信号量有可用的许可并且当前线程未被</span><a rel="nofollow">中断</a>,则从此信号量获取一个许可。</p></td> > </tr> > </tbody> > </table> **示例:3个灯 10个人** > 房间,有多少人都能装,线程数动态变化,来一个人产生一个线程 > > ExecutorService service = Exccutors.newCachedThreadPool(); > > final Semaphore sp = new Semaphore(3);灯的个数 指定只有3个 > > //3个灯,来了5个人,有2个人要等,其中有一个办完事走了,等待的2个哪个先上呢?默认的构造方法不管,谁抢到了谁上。用new Semaphore(3, true)就可以保证先来的先上。 > > //将灯的个数设置为1就可以达到互斥效果,每次只能有一个线程运行 > > for (int i=0; i<10; i++)来了10个人 > > {//人的任务 抢灯 > > Runnable runnable = new Runnable() > > { > > public void run() > > { > > sp.acquire();//抢坑了 会抛中断异常 > > }//有人占住灯了,给出提示 > > SOP(currentThreadName+进入,当前已有(3-sp.availablePermits())个人了) > > Thread.sleep(5000)灯办事 > > //办完事打声招呼 > > SOP(ThreadName即将离开) > > 释放坑的占有权 > > sp.release(); > > SOP(ThreadName已经走了,还有sp.availablePermits()个灯可用) > > } > > 开始任务吧 > > service.execute(runnable) > > } > > > > > 传统互斥只能内部释放锁this.unlock(),进去this.lock()晕倒了别人就没法进去了;用信号灯可以外部释放,其他线程可以释放再获取sp.release() sp.acquire()。 > > >
还没有评论,来说两句吧...