在以前的操作系统学习中,为了实现进程的同步,需要设置信号量执行P、V操作来实现。在Java中,线程之间的同步也是使用信号量Semaphore类来实现的。线程访问临界资源(需互斥访问的那段代码)的时候,如果信号量(计数器)大于0,则允许进入临界资源,若信号量小于等于0,则线程阻塞等待另外的线程释放临界资源。Java 并发库的Semaphore 可以很轻松完成信号量控制,Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。比如在Windows下可以设置共享文件的最大客户端访问个数。
Semaphore概述
---通常把一个非负整数称为Semaphore,表示为S.
S可以理解为可用的资源数量.这里不涉及进程问题,所以就假定S>=0.
---S实现的同步机制表示为PV原语操作
P(S):若S=0,线程进入等待队列;否则,—S;
V(S):++S,唤醒处于等待中的线程.
信号量的特性如下:信号量是一个非负整数,所有通过它的线程都会将该整数减一,当该整数值为零时,所有试图通过它的线程都将处于等待状态。在信号量上我们定义两种操作: Acquire(等待) 和 Release(释放)。当一个线程调用Acquire(等待)操作时,它要么通过然后将信号量减一,要么一直等下去,直到信号量大于一或超时。Release(释放)实际上是在信号量上执行加操作,加操作实际上是释放了由信号量守护的资源。
在java中,还可以设置该信号量是否采用公平模式,如果以公平方式执行,则线程将会按到达的顺序(FIFO)执行,如果是非公平,则可以后请求的有可能排在队列的头部。
1、Semaphore(int permits, boolean fair):创建具有给定的许可数和给定的公平设置的Semaphore。在该构造函数中,permits指定了计数器的初始值,如果permits=1,则说明在某个时间间隔之内,只允许一个线程访问临界资源,默认情况下(fair默认为false,即不采用公平策略),等待线程以随机顺序获取临界资源(当permits>0时),若将fair设置为true,则采用公平策略,等待线程已他们要求的访问顺序获取临界资源。
2、Semaphore类中,请求获取临界资源的方法如下所示:tryAcquire、acquire;
3、Semaphore类中,请求获取临界资源的方法如下所示:release;
示例程序:
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class Bank {
private final static int COUNT = 100;
private final static Semaphore semaphore = new Semaphore(3, true);
public static void main(String[] args) {
for (int i = 0; i < COUNT; i++) {
final int count = i;
new Thread(new Runnable() {
@Override
public void run() {
try {
// 没有可用的资源就等待一段时间,如果在该时间内仍未获取到所需要的资源semaphore,就返回false
// 这里只申请一个,如果申请多个需要调用其他的请求方法
if (semaphore.tryAcquire(10, TimeUnit.MILLISECONDS)) {
try {
Teller.getService(count);
} finally {
// 释放所获取到的资源semaphore
semaphore.release();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
class Teller {
static public void getService(int i) {
System.out.println("serving:" + i);
try {
Thread.sleep((long) (Math.random() * 100));
} catch (InterruptedException e) {
}
}
}
运行结果(一种)
serving:10
serving:0
serving:11
serving:9
serving:8