信号量
- 信号量模型,一个计数器,一个等待队列,三个方法。三个方法init(),up(),down()
三个方法
- init()设置计数器的初始值。
- up()计数器值加1如果计数器值小于等于0,唤醒等待队列中一个线程,并从等待队列中移除。
- down()计数器值减一如果值小于0,那么阻塞此线程,否则当前线程继续执行。
- 上述千万别和消费者生产者模型混淆。
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) {
// 移除等待队列中的某个线程并唤醒线程
}
}
}
限流器
- 举例:创建N个对象,同时间最多只有m个线程执行,N初始一般都比m大(不然也没必要了),代码实现如下。
import java.util.concurrent.*;
import java.util.function.Function;
/**
* @Author : zzz
* @Date : Created in 15:51 2020/3/5
* @Description : 创建20个线程共用两个student对象典型场景我遇到的:mysql中配置多个查询sql,一定条件下触发,
* 执行sql后生成文件比如其中一条sql为select id from student where id limit 100,10
* 如果有多少sql多线程并行执行,这肯定不行,数据库性能受影响,可以用这个模型解决也就是限流,每次只有几个线程去执行。
*/
public class SemphoreDemo {
private final ArrayBlockingQueue<Student> pool;
private final Semaphore semaphore;
public SemphoreDemo(int size) {
pool = new ArrayBlockingQueue<Student>(size);
semaphore = new Semaphore(size);
for (int i=0;i<size;i++) {
Student student = new Student(i, "xiaoming_" + i);
pool.add(student);
}
}
public Integer execute(Function<Student, Integer> function) throws InterruptedException {
Student student = null;
semaphore.acquire();
try {
student = pool.poll();
return function.apply(student);
} finally {
semaphore.release();
pool.add(student);
}
}
public static void main(String[] args) throws Exception {
SemphoreDemo demo = new SemphoreDemo(2);
CountDownLatch countDownLatch = new CountDownLatch(20);
for (int i=0;i<20;i++) {
new Thread(()->{
try {
demo.execute(e -> {
System.out.println(e);
return e.getAge();
});
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
countDownLatch.await();
System.out.println("执行完毕!");
//Thread.sleep(3000);
}
private static class Student {
private int age;
private String name;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return this.age;
}
public String getName() {
return this.name;
}
@Override
public String toString() {
return "name=" + name + ", age=" + age;
}
}
}
执行结果如下,可以看到对象用完了马上还回去继续使用,某一时刻最多只有两个线程能够执行。实际上线程池也可以实现此功能。
name=xiaoming_0, age=0
name=xiaoming_1, age=1
name=xiaoming_0, age=0
name=xiaoming_1, age=1
name=xiaoming_0, age=0
name=xiaoming_1, age=1
name=xiaoming_0, age=0
name=xiaoming_1, age=1
name=xiaoming_0, age=0
name=xiaoming_1, age=1
name=xiaoming_0, age=0
name=xiaoming_1, age=1
name=xiaoming_0, age=0
name=xiaoming_1, age=1
name=xiaoming_0, age=0
name=xiaoming_1, age=1
name=xiaoming_0, age=0
name=xiaoming_1, age=1
name=xiaoming_0, age=0
name=xiaoming_1, age=1
执行完毕!