文章目录



一.信号量

Semaphore也是一个线程同步的辅助类,可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。

二.信号量模型

概况为:一个计数器,一个等待队列,三个方法。在信号模型里,计数器和等待队列对外是透明

,所以只能通过信号模型提供的三个方法来访问它们,这三个方法分别是:init()、down()、up()。

Java并发Semaphore_java

这三个方法详细语义:


  • init():设置计数器初始值。
  • down():计数器的值减1;如果此时计数器的值小于0,则当前线程将阻塞,否则当前线程可以继续执行。
  • up():计数器的值加1;如果此时计数器的值小于或者等于0,则唤醒等待队列中一个线程,并将其从等待队列中移除。

这里提到的 init()、down() 和 up() 三个方法都是原子性的,并且这个原子性是由信号量模型的实现方保证的。在 Java SDK 里面,信号量模型是由 java.util.concurrent.Semaphore 实现的,Semaphore 这个类能够保证这三个方法都是原子操作。

// Java SDK 并发包里,down() 和 up() 对应的则是 acquire() 和 release()。
class Semaphore{
// 计数器
int count;
// 等待队列
Queue queue;
// 初始化操作
Semaphore(int c){
this.count=c;
}
//
void down(){
this.count--;
if(this.count<0){
//将当前线程插入等待队列
//阻塞当前线程
}
}
void up(){
this.count++;
if(this.count<=0) {
//移除等待队列中的某个线程T
//唤醒线程T
}
}
}

三.使用信号量

3.1 示例-累加器

count+=1操作是个临界区,只允许一个线程执行,也就是说要保证互斥。

public class SemaphoreTest {
static int count;
static final Semaphore s = new Semaphore(1);
static void addOne(){
try {
s.acquire();
count +=1;
}catch (Exception e){
e.printStackTrace();
}finally {
s.release();
}
}
public static void main(String[] args) {
Thread t1 = new Thread(()->{
SemaphoreTest.addOne();
});
Thread t2 = new Thread(()->{
SemaphoreTest.addOne();
});
Thread t3 = new Thread(()->{
SemaphoreTest.addOne();
});

t1.start();
t2.start();
t3.start();
try {
t3.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(SemaphoreTest.count);
}
}

3.2 示例-限流器

Semaphore 有个功能是Lock不容易实现,就是Semaphore可以允许多个线程访问一个临界区。

例如:对象池,线程池,都会用到类似功能。

/**
* 对象池,指的一次性创建出N个对象,之后所有线程重复利用这N个
* 对象,当然对象在被释放前,也是不容许其他线程使用。 *
* 对象的互斥性
*/
public class ObjPool<T,R> {
//用信号量实现限流器
final List<T> pool;
//构造函数
final Semaphore sem;
public ObjPool(T[] tArray){
pool = new Vector<T>(){};
int size = tArray.length;
for (int i = 0; i < tArray.length; i++) {
pool.add(tArray[i]);
}
sem = new Semaphore(size);
}
R exec(Function<T,R> func) throws InterruptedException {
T t = null;
try {
sem.acquire();
t = pool.remove(0);
return func.apply(t);
}finally {
pool.add(t);
sem.release();
}
}
public static void main(String[] args) {
String[] mess = new String[10];
for (int i = 0; i < 10; i++) {
mess[i] = "obj_"+i;
}
ObjPool<String, String> objPool = new ObjPool<>(mess);
for (int i = 0; i < 100; i++) {
Thread t1= new Thread(()->{
try {
objPool.exec(t -> {
System.out.println("当前线程id:"+Thread.currentThread().getId()+",当前获取到的对象:"+t);
return t;
});
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}


参考


《Java并发编程实战》


公众号


Java并发Semaphore_java_02

微信公众号(bigdata_limeng)