信号量模型
java在1.5以后引入了Semaphore 信号量模型,放在concurrent包下面用来解决同步协作问题。
即解决两个基本问题
- 多个共享资源互斥使用
- 并发线程数的控制
“计数信号量(Counting Semaphore)用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量。计数信号量还可以用来实现某种资源池,或者对容器施加边界。”
“Semaphore中管理着一组虚拟的许可(permit),许可的出事量可以通过构造函数来指定。在执行操作是可以首先获得许可(只要还有剩余的许可),并在使用以后释放许可。如果没有许可,那么acquire将阻塞直到有许可(或者直到被中断或者操作超时)。release方法将返回一个许可给信号量。计算信号量的一种简化形式是二值信号量,即初始值为1的Semaphore。二值信号量可以用作互斥(mutex),并具备不可重入的加锁语义:谁拥有这个唯一的许可,谁就拥有了互斥锁。”
“概念上讲,一个信号量管理许多的许可证(permit)。为了通过信号量,线程通过调用acquire请求许可。其实没有实际的许可对象,信号量仅维持一个计数。许可的数目是固定的,由此限制了通过的线程数量。其他线程可以通过调用release释放许可。而且,许可必须由获取它的线程释放。事实上,任何线程都可以释放任意数目的许可,这可能会增加许可数目以至于超出初始数目。”
Semaphore的使用
先看一个例子:
public class SemaphoreDemo {
private int n;
public SemaphoreDemo(int n) {
this.n = n;
}
Semaphore ever = new Semaphore(1);
int i = 1;
public void even(IntConsumer printNumber) throws InterruptedException {
while (i <= n) {
ever.acquire();
if (i % 2 == 1 && i <= n) {
printNumber.accept(i);
i++;
}
ever.release();
}
}
public void odd(IntConsumer printNumber) throws InterruptedException {
while (i <= n) {
ever.acquire();
if (i % 2 == 0 && i <= n) {
printNumber.accept(i);
i++;
}
ever.release();
}
}
public static void main(String[] args) {
SemaphoreDemo semaphoreDemo = new SemaphoreDemo(100);
new Thread(() -> {
try {
semaphoreDemo.even(i -> System.out.println("" + i));
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
semaphoreDemo.odd(i -> System.out.println("" + i));
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
这个例子调用两个线程分表交替打印奇偶数。这就是利用了信号对资源共享进行互斥使用。
第二个例子:
public static void main(String[] args){
Semaphore se=new Semaphore(5);
for(int i=0;i<=20;i++){
new Thread(()->{
try {
se.acquire();
System.out.println(Thread.currentThread().getName()+"\t 我占了个座");
TimeUnit.SECONDS.sleep((int)Math.random()*10+1);
System.out.println(Thread.currentThread().getName()+"\t 我用完了");
}catch (Exception e){
e.printStackTrace();
}finally {
se.release();
}
}).start();
}
}
通过令牌数量的控制,限制了并发线程的数量。