每篇一格言:
秩序就是正确的规律和事物永久的合理性
——Henry Fielding
目录
前言
lock操作在某一时刻只允许一个任务访问资源(例如写文件)。而Semaphore允许多个任务在同一时刻访问资源。本篇以Android代码为实例学习Semaphore的使用方法。
1.Semaphore概念
Semaphore翻译为信号量,但是这个名称不是很直观。更直观的称呼是许可证拥有者。下面我们用更形象的方式做类比。
task——汽车
访问资源 —— 上高速公路
Semaphore —— 高速公路收费站
我们知道,汽车驶入高速公路前需请求收费站发放通行证,驶出高速前归还通行证。为了避免高速公路被挤爆,所以通行证是有限的。
类似的,task想要访问资源需先用acquire()获得许可证,访问结束后release()归还许可证。
Semaphore只需维护当前许可证的个数,实现起来效率很高。
JDK1.8中的注释:
* A counting semaphore. Conceptually, a semaphore maintains a set of
* permits. Each {@link #acquire} blocks if necessary until a permit is
* available, and then takes it. Each {@link #release} adds a permit,
* potentially releasing a blocking acquirer.
* However, no actual permit objects are used; the {@code Semaphore} just
* keeps a count of the number available and acts accordingly.
*
* <p>Semaphores are often used to restrict the number of threads than can
* access some (physical or logical) resource.
2.Semaphore使用方法
在JDK中还有一段示例代码:
* class Pool {
* private static final int MAX_AVAILABLE = 100;
* private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
*
* public Object getItem() throws InterruptedException {
* available.acquire();
* return getNextAvailableItem();
* }
*
* public void putItem(Object x) {
* if (markAsUnused(x))
* available.release();
* }
*
* // Not a particularly efficient data structure; just for demo
*
* protected Object[] items = ... whatever kinds of items being managed
* protected boolean[] used = new boolean[MAX_AVAILABLE];
*
* protected synchronized Object getNextAvailableItem() {
* for (int i = 0; i < MAX_AVAILABLE; ++i) {
* if (!used[i]) {
* used[i] = true;
* return items[i];
* }
* }
* return null; // not reached
* }
*
* protected synchronized boolean markAsUnused(Object item) {
* for (int i = 0; i < MAX_AVAILABLE; ++i) {
* if (item == items[i]) {
* if (used[i]) {
* used[i] = false;
* return true;
* } else
* return false;
* }
* }
* return false;
* }
* }}</pre>
这段代码非常直观,getItem前要先获取许可证(调用acquire()),putItem后增加一个许可证(调用release())。假设每个Item代表1元硬币,pool就相当于能放100个硬币的存钱罐。如果只取不存,最多只能花100元就没钱了。很显然,这段代码诠释了“量入为出”的含义。
new Semaphore(MAX_AVAILABLE, true)表示最多有MAX_AVAILABLE(这里是100)个许可证。
思考:如果改成new Semaphore(20, true)会怎样呢?
这表示虽然你有100元,但是最多只能花20元。可以看出,Semaphore在初始化时已经对访问资源的线程数量进行了限制。
下面具体讨论几个重要方法
new Semaphore(MAX_AVAILABLE, true)
初始化100个许可证,true表示按公平模式,也就是FIFO
acquire();
阻塞的获取一个许可证。具体是:
1 如果存在并立即返回了一个许可证,则当前许可证数量减1.
2.如果没有许可证,当前线程阻塞,直到:
2.1.其他线程释放了一个许可证,并且当前线程排在等待队列最前,
2.2 或者其他线程中断了当前线程。
release()
释放一个许可证给semaphore
3.Android中Semaphore的使用
下面我们具体看看Semaphore在Android中使用的例子。
代码路径:
SecurityLogMonitor.java (frameworks\base\services\devicepolicy\java\com\android\server\devicepolicy)
1.初始化一个Semaphore:
/** Semaphore used to force log fetching on request from adb. */
private final Semaphore mForceSemaphore = new Semaphore(0 /* permits */);
这里许可证数量是0.
2. SecurityLogMonitor是一个Runnable,在它的run方法中尝试获取一个许可证:
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
final ArrayList<SecurityEvent> newLogs = new ArrayList<>();
while (!Thread.currentThread().isInterrupted()) {
try {
//mark 1:
final boolean force = mForceSemaphore.tryAcquire(POLLING_INTERVAL_MS, MILLISECONDS);
getNextBatch(newLogs);
。。。
//mark 2:
notifyDeviceOwnerIfNeeded(force);
}
。。。
mark 1:
tryAcquire(POLLING_INTERVAL_MS, MILLISECONDS)表示在POLLING_INTERVAL_MS时间以内阻塞并等待获取一个许可证。它和acquire的区别是阻塞有时间限制,超过设定的时间则不再阻塞,继续执行。
mark 2:
如果在限制时间内得到许可证,则force值是true,表示允许log;否则不允许log。
由于Semaphore在初始化时许可证数量是0,所以要想成功获得许可证,必然需要其他线程释放一个许可证。
3. forceLogs()方法释放一个许可证
public long forceLogs() {
final long nowNanos = System.nanoTime();
// We only synchronize with another calls to this function, not with the fetching thread.
synchronized (mForceSemaphore) {
final long toWaitNanos = mLastForceNanos + FORCE_FETCH_THROTTLE_NS - nowNanos;
if (toWaitNanos > 0) {
return NANOSECONDS.toMillis(toWaitNanos) + 1; // Round up.
}
mLastForceNanos = nowNanos;
// There is a race condition with the fetching thread below, but if the last permit is
// acquired just after we do the check, logs are forced anyway and that's what we need.
if (mForceSemaphore.availablePermits() == 0) {
mForceSemaphore.release();
}
return 0;
}
}
这个方法调用mForceSemaphore.release();释放了一个许可证。
而调用forceLogs()的是DevicePolicyManagerService,如果感兴趣可以查看这段代码:
DevicePolicyManagerService.java (code\frameworks\base\services\devicepolicy\java\com\android\server\devicepolicy)
本文为博主原创。看完请点赞/评论,让我们共同进步~