Scala并发编程实战 4:Semaphore 信号量模型

Semaphore信号量模型,是一种通过维护计数器数值来控制并发数量的模型,Lock实现的互斥锁只允许一个线程访问临界区,而Semaphore允许有限多个线程访问临界区。

什么情况需要允许多个线程同时访问?最常见的需求就是池化资源,连接池、线程池、对象池等等。

java.util.concurren.Semaphore 是JDK中的实现类,常用方法有这些:

1
2
3
public Semaphore(int permits);
public void acquire() throws InterruptedException;
public void release();

构造函数可以传入permits,设置计数器的初始值,表示可以同时获取锁的线程数。
acquire函数用于获取锁,release相反。

下面利用Semaphore实现一个对象池,里面可以存放任意需要复用的对象,如果存放的是连接对象,就变成了连接池。
下面例子往对象池存放了几个字符串对象,再用若干线程同时去申请这个字符串资源。

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
42
43
44
45
package io.github.liam8.con

import java.text.SimpleDateFormat
import java.util.Date
import java.util.concurrent.{ConcurrentLinkedDeque, Semaphore, TimeUnit}

import collection.JavaConversions._

object SemaphoreDemo {

def main(args: Array[String]): Unit = {
val pool = new SemaphoreDemo[String](2, List("aaaa", "bbbbb"))
val formatDate = (date: Date) => new SimpleDateFormat("HH:mm:ss.SSS").format(date)
for (i <- 1 to 10) {
new Thread {
override def run(): Unit = {
pool.exec { e =>
println(s"${formatDate(new Date)} thread $i using $e")
TimeUnit.SECONDS.sleep(3)
println(s"${formatDate(new Date)} thread $i done with $e")
}
}
}.start()
}
}

}

class SemaphoreDemo[T](size: Int, items: List[T]) {

val pool: ConcurrentLinkedDeque[T] = new ConcurrentLinkedDeque[T](items)

val semaphore: Semaphore = new Semaphore(size)

def exec(func: T => Unit): Unit = {
semaphore.acquire()
val t = pool.pop()
try {
func(t)
} finally {
pool.add(t)
semaphore.release()
}
}
}

output

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
09:56:52.313 thread 4 using bbbbb
09:56:52.313 thread 2 using aaaa
09:56:55.351 thread 2 done with aaaa
09:56:55.351 thread 4 done with bbbbb
09:56:55.352 thread 1 using aaaa
09:56:55.352 thread 3 using bbbbb
09:56:58.353 thread 1 done with aaaa
09:56:58.353 thread 3 done with bbbbb
09:56:58.353 thread 5 using bbbbb
09:56:58.353 thread 6 using aaaa
09:57:01.354 thread 6 done with aaaa
09:57:01.354 thread 5 done with bbbbb
09:57:01.355 thread 7 using aaaa
09:57:01.355 thread 8 using bbbbb
09:57:04.359 thread 7 done with aaaa
09:57:04.359 thread 8 done with bbbbb
09:57:04.360 thread 9 using aaaa
09:57:04.360 thread 10 using bbbbb
09:57:07.363 thread 9 done with aaaa
09:57:07.363 thread 10 done with bbbbb

本文代码

Github仓库