(1)信号灯,或者说限流器

Semaphore由迪杰斯特拉提出的,一直被使用,直到管程被提出,

首先是信号量模型

在信号量中,可以简述为 一个计数器,一个等待队列,三个方法

计数器和等待队列是必要的,透明的,信号量模型的三个方法为 init() down() up()

init为,设置计数器的初始值

down()将计数器的值进行减一的操作,如果小于了0,将线程阻塞

up() 计数器的值加一操作,如果仍然小于或者等于0,唤醒一个等待队列中的线程

图片

这三个方法的值的设置,都是保证了其原子性的基础上

接下来是一个简单的Semaphore实现,代码如下:

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

}

}

}

down和up最早被称为P和V操作,又名PV原语,

在Java中信号量的实现在Java.util.concurrent.Semaphore实现的

其中的对应方法为,

down() -> acquire()

up() -> release()

如何去使用信号量呢,很简单,就是在累加器上,通过控制其中的一个信号量,在一个临界区之间徘徊,保证线程的运行和阻塞之间的切换

在进入临界区之前进行一下down() 在出区的时候进行一下up()

static int count;

//初始化信号量

static final Semaphore s = new Semaphore(1);

//用信号量保证互斥

static void addOne () {

s.acquire();

try {

count += 1;

} finally {

s.release();

}

}

其内部原理很简单,因为对信号量的操作是一个原子操作,那么只能有一个线程能对信号量操作

那么对于A线程,线程大于等于0,继续执行,对于线程B,线程小于0,进入阻塞状态

对于A线程,完成后,将计数器加到0,进行唤醒一个等待队列中的线程,由此保证了互斥性

程将计数器减为0

快速实现一个限流器

对于Semaphore,相比起Lock的功能,提供了一个新的思路,那就是允许 多个线程访问一个临界区

常见的就是各类池化的资源,也是一次性创建出N个对象,重复利用这些对象,在释放前,不允许其他的线程使用,也就是不允许大于N个对象访问

class ObjPool<T, R> {

final List<T> pool;

// 用信号量实现限流器

final Semaphore sem;

// 构造函数

ObjPool(int size, T t){

pool = new Vector<T>(){};

for(int i=0; i<size; i++){

pool.add(t);

}

sem = new Semaphore(size);

}

// 利用对象池的对象,调用func

R exec(Function<T,R> func) {

T t = null;

sem.acquire();

try {

t = pool.remove(0);

return func.apply(t);

} finally {

pool.add(t);

sem.release();

}

}

}

// 创建对象池

ObjPool<Long, String> pool =

new ObjPool<Long, String>(10, 2);

// 通过对象池获取t,之后执行

pool.exec(t -> {

System.out.println(t);

return t.toString();

});

上面就是利用了信号量来实现的一个限流器

发表评论

邮箱地址不会被公开。 必填项已用*标注