简单说一下Semaphore的使用
Semaphore又称信号量,是操作系统中的一个概念,在Java并发编程中,信号量控制的是线程并发的数量。Semaphore是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。把它比作是控制流量的红绿灯,比如XX马路要限制流量,只允许同时有一百辆车在这条路上行使,其他的都必须在路口等待,所以前一百辆车会看到绿灯,可以开进这条马路,后面的车会看到红灯,不能驶入XX马路,但是如果前一百辆中有五辆车已经离开了XX马路,那么后面就允许有5辆车驶入马路,这个例子里说的车就是线程,驶入马路就表示线程在执行,离开马路就表示线程执行完成,看见红灯就表示线程被阻塞,不能执行。
应用场景
Semaphore可以用于做流量控制,特别公用资源有限的应用场景,比如数据库连接。假如有一个需求,要读取几万个文件的数据,因为都是IO密集型任务,我们可以启动几十个线程并发的读取,但是如果读到内存后,还需要存储到数据库中,而数据库的连接数只有10个,这时我们必须控制只有十个线程同时获取数据库连接保存数据,否则会报错无法获取数据库连接。这个时候,我们就可以使用Semaphore来做流控。更通俗来说,比如说操场上有5个跑道,一个跑道一次只能有一个学生在上面跑步,一旦所有跑道在使用,那么后面的学生就需要等待,直到有一个学生不跑了。
上代码:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
public class SemaphoreTest {
private final static int threadNum = 200;
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool(); //创建一个线程池
final Semaphore semaphore = new Semaphore(5); //创建一个允许20个线程同时执行的Semaphore
for(int i = 0; i < threadNum; i++){
final int finalI = i;
executorService.execute(() -> {
try {
semaphore.acquire(); //获取一个执行许可
//也可以semaphore.acquire(5); 表示一次获取5个许可
test(finalI); //调用test方法 每次打印当前的i
semaphore.release(); //释放一个执行许可
//也可以semaphore.release(5); 表示一次释放5个许可
//当每次获取的许可数和new Semaphore(i)中的i相等时 就和单线程环境一样了,这里只是举个例子
} catch (Exception e) {
e.printStackTrace();
}
});
}
executorService.shutdown();
}
private static void test(int num) throws Exception{
System.out.println(num + "--" + new SimpleDateFormat(" HH:mm:ss").format(new Date()));
Thread.sleep(1000);
}
}
上面代码的执行结果是:
2-- 15:59:28
5-- 15:59:28
1-- 15:59:28
0-- 15:59:28
4-- 15:59:28
6-- 15:59:29
8-- 15:59:29
10-- 15:59:29
12-- 15:59:29
16-- 15:59:29
20-- 15:59:30
24-- 15:59:30
28-- 15:59:30
32-- 15:59:30
36-- 15:59:30
.....
这里只贴出一部分 但是可以看出来 每秒都是只输出5个 因为test()
方法中线程睡眠1秒
而每秒只输出5个是因为new Semaphore(5);
控制每次只能有5个线程通过
还有另一种情况,就是当前允许的并发数是5
,超过5个之后就丢弃,不去管超过的,这种Semaphore也是可以控制的
看例子:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
public class SemaphoreTest {
private final static int threadNum = 200;
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool(); //创建一个线程池
final Semaphore semaphore = new Semaphore(5); //创建一个允许20个线程同时执行的Semaphore
for(int i = 0; i < threadNum; i++){
final int finalI = i;
executorService.execute(() -> {
try {
//可以设置尝试获取时间semaphore.tryAcquire(1, TimeUnit.SECONDS)
//这样表示尝试获取许可 尝试时间1秒 1秒内获取到许可就执行 获取不到就不管了
if(semaphore.tryAcquire()) { //尝试获取一个许可
test(finalI); //调用test方法 每次打印当前的i
semaphore.release(); //释放一个执行许可
}
} catch (Exception e) {
e.printStackTrace();
}
});
}
executorService.shutdown();
}
private static void test(int num) throws Exception{
System.out.println(num + "--" + new SimpleDateFormat(" HH:mm:ss").format(new Date()));
Thread.sleep(1000);
}
}
上面这个代码输出结果是:
2-- 16:18:50
3-- 16:18:50
4-- 16:18:50
0-- 16:18:50
1-- 16:18:50
就只有5个输出 没有其他的了 因为在semaphore.tryAcquire()
这个尝试获取许可的时候 同时只允许5个 所以就只输出的5个数字 其他的都没有执行
好了 这就是简单的semaphore使用
个人浅薄理解,欢迎补充
点击链接加入群聊【Java技术学习闲聊群】:https://jq.qq.com/?_wv=1027&k=59emCBA