java中Semaphore的作用是什么?如何使用?
信号量
简单理解,java中的Semaphore类的作用就是限制某个资源最多同时允许设定数量的线程访问.
这个资源可以是一个代码块,可以是数据库连接.
使用示例
- 使用多线程并发执行四次任务. 使用信号量限制do something代码块最多有两个线程并发访问.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
@Test
public void test1() {
ExecutorService executorService = Executors.newCachedThreadPool();
// 设置信号量为2
Semaphore semaphore = new Semaphore(2);
// 并发执行4次任务
for (int i = 0; i < 4; i++) {
executorService.execute(() -> {
try {
System.out.println("task start time:"+new Date());
semaphore.acquire();
// do something
Thread.sleep(1000);
// do something
System.out.println("task end time:"+new Date());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
});
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
|
输出结果
1
2
3
4
5
6
7
8
|
task start time:Sun Apr 19 21:28:22 CST 2020
task start time:Sun Apr 19 21:28:22 CST 2020
task start time:Sun Apr 19 21:28:22 CST 2020
task start time:Sun Apr 19 21:28:22 CST 2020
task end time:Sun Apr 19 21:28:23 CST 2020
task end time:Sun Apr 19 21:28:23 CST 2020
task end time:Sun Apr 19 21:28:24 CST 2020
task end time:Sun Apr 19 21:28:24 CST 2020
|
可以看到四个任务并发开始执行.同一秒开始启动.但是由于do something代码块使用信号量进行了限制.do something代码块的实际执行是先有两个线程
获得了许可并发执行了do something代码块并释放了许可其他两个线程获得许可才开始执行do something代码块.
- 限制数据库连接的访问.设置连接池最大连接数量,当没有空闲连接的时候,新的获取数据库连接的请求进行等待,等待超过一定时间进行放弃.
定义数据库连接类.具有执行sql和关闭连接方法.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
/**
* 数据库连接
*/
public class Connection{
// 信号量
private Semaphore semaphore;
public Connection(Semaphore semaphore) {
this.semaphore = semaphore;
}
/**
* 执行sql
* @param sql
*/
public void runSql(String sql){
try {
Thread.sleep(1000);
System.out.println(new Date()+" exec sql:"+sql);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 关闭数据库连接
*/
public void closeConnection(){
semaphore.release();
}
}
|
定义数据库连接池.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
public class DataBaseConnPool{
// 数据库链接最大连接数
private Integer maxPoolSize;
// 获取数据库连接的超时时间.
private Long timeOut;
// 信号量
private Semaphore semaphore;
public DataBaseConnPool(Integer maxPoolSize, Long timeOut) {
this.maxPoolSize = maxPoolSize;
this.timeOut = timeOut;
semaphore = new Semaphore(maxPoolSize);
}
/**
* 获取数据库连接
* @return
*/
public Connection getConnection(){
boolean getConn = false;
try {
getConn = semaphore.tryAcquire(timeOut, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (getConn){
return new Connection(semaphore);
}else {
throw new RuntimeException("获取数据库链接超时!");
}
}
}
|
创建连接池执行sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@Test
public void test2() {
ExecutorService executorService = Executors.newCachedThreadPool();
// 连接池数量设置为5,超时时间是6s
DataBaseConnPool dataBaseConnPool = new DataBaseConnPool(5, 6000L);
// 并发执行36次任务
for (int i = 0; i < 36; i++) {
executorService.execute(() -> {
// 获取连接
Connection connection = dataBaseConnPool.getConnection();
// 执行sql
connection.runSql("select name from tbl_user");
// 关闭连接
connection.closeConnection();
});
}
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
|
输出结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
Sun Apr 19 22:30:59 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 31 availablePool: 0
Sun Apr 19 22:30:59 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 31 availablePool: 0
Sun Apr 19 22:30:59 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 31 availablePool: 0
Sun Apr 19 22:30:59 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 31 availablePool: 0
Sun Apr 19 22:30:59 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 27 availablePool: 0
Sun Apr 19 22:31:00 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 26 availablePool: 0
Sun Apr 19 22:31:00 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 26 availablePool: 0
Sun Apr 19 22:31:00 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 26 availablePool: 0
Sun Apr 19 22:31:00 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 26 availablePool: 0
Sun Apr 19 22:31:00 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 22 availablePool: 0
Sun Apr 19 22:31:01 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 21 availablePool: 0
Sun Apr 19 22:31:01 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 21 availablePool: 1
Sun Apr 19 22:31:01 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 19 availablePool: 0
Sun Apr 19 22:31:01 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 21 availablePool: 0
Sun Apr 19 22:31:01 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 17 availablePool: 0
Sun Apr 19 22:31:02 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 16 availablePool: 0
Sun Apr 19 22:31:02 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 16 availablePool: 0
Sun Apr 19 22:31:02 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 15 availablePool: 1
Sun Apr 19 22:31:02 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 14 availablePool: 1
Sun Apr 19 22:31:02 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 16 availablePool: 0
Sun Apr 19 22:31:03 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 11 availablePool: 0
Sun Apr 19 22:31:03 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 11 availablePool: 0
Sun Apr 19 22:31:03 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 11 availablePool: 0
Sun Apr 19 22:31:03 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 9 availablePool: 0
Sun Apr 19 22:31:03 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 11 availablePool: 1
Sun Apr 19 22:31:04 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 6 availablePool: 0
Sun Apr 19 22:31:04 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 6 availablePool: 1
Sun Apr 19 22:31:04 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 5 availablePool: 1
Sun Apr 19 22:31:04 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 5 availablePool: 2
Sun Apr 19 22:31:04 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 6 availablePool: 0
Exception in thread "pool-1-thread-36" java.lang.RuntimeException: 获取数据库链接超时!
at com.kklt.test.juc.SemaphoreTest$DataBaseConnPool.getConnection(SemaphoreTest.java:101)
at com.kklt.test.juc.SemaphoreTest.lambda$test2$1(SemaphoreTest.java:55)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Sun Apr 19 22:31:05 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 0 availablePool: 0
Sun Apr 19 22:31:05 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 0 availablePool: 1
Sun Apr 19 22:31:05 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 0 availablePool: 1
Sun Apr 19 22:31:05 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 0 availablePool: 1
Sun Apr 19 22:31:05 CST 2020 exec sql:select name from tbl_user getWaitQueueLength: 0 availablePool: 3
|
可以看到,同时可以使用的数据库连接资源为5.获取数据库连接等待超过六秒的线程获取失败.