Semaphore
确实一次获得所有许可,否则它不会是一个真正的semaphore。但是:Java版本也有一个内部等待队列。并且该队列的行为是不是服务最适合当前免费资源但或多或少收集许可证,直到队列中的第一个请求可以被允许。但在线程进入之前,如果可用的许可允许线程根本避免进入队列,则检查完成。
我已经修改了你的代码,以显示该队列的行为:
import java.util.concurrent.*;
public class SemaphoreTest{
static Semaphore s = new Semaphore(0);
public void fun(final char c, final int r) throws Exception {
new Thread(new Runnable(){
public void run(){
try{
System.out.println("acquire "+r);
s.acquire(r);
System.out.println(c+"_"+r);
} catch(Exception e){ e.printStackTrace(); }
}
}).start();
Thread.sleep(500);
}
public static void main(String[]args) throws Exception{
SemaphoreTest f = new SemaphoreTest();
f.fun('B',2);
f.fun('F',6);
f.fun('A',1);
f.fun('C',3);
f.fun('D',4);
f.fun('E',5);
while(s.hasQueuedThreads()){
Thread.sleep(1000);
System.out.println("release "+1+", available "+(s.availablePermits()+1));
s.release(1);
}
}
}
基本上下面的更改已完成:
- 从0开始的许可 - 让任何人第一次进入队列。
- 通过在
Thread.start
之后给每个线程500ms的时间“定义”排队顺序。
- 每个线程都会调用
acquire
而不是release
。
- 主线程将以一个许可证缓慢地将信号量反馈给另一个许可证。
这将使该输出确定性:
acquire 2
acquire 6
acquire 1
acquire 3
acquire 4
acquire 5
release 1, available 1
release 1, available 2
B_2
release 1, available 1
release 1, available 2
release 1, available 3
release 1, available 4
release 1, available 5
release 1, available 6
F_6
release 1, available 1
A_1
release 1, available 1
release 1, available 2
release 1, available 3
C_3
release 1, available 1
release 1, available 2
release 1, available 3
release 1, available 4
D_4
release 1, available 1
release 1, available 2
release 1, available 3
release 1, available 4
release 1, available 5
E_5
release 1, available 1
这意味着:每一个线程被唤醒,如果
这确实解决了它,但我想知道为什么它是必要的。如果在打印B_2之后卡住了,并且在主线程中,经过一段时间,我打印出可用许可证的数量,它会打印3.那么为什么不允许线程C继续? – Vlad
@Vlad:我的猜测是'3'没有反映出'D'可能已经为其获取(4)'呼叫保留了一些许可。 – NPE
我想你可能是对的。我没有仔细阅读发布的文档。 [链接](http://download.oracle.com/javase/6/docs/api/java/util/concurrent/Semaphore.html#release(INT))。我曾假设这个实现会围绕这些线程循环,并试图找到一个可以真正唤醒的实现。 – Vlad