信号量(Semaphores)


前言

记得当年在学习《操作系统》时,对信号量和P,V操作这一块一直搞不太清楚,书中的伪代码着实令人费解(看得懂,就是下笔就忘)。上学时未解决的问题,现在终究还是要解决呀。今天就聊聊java.util.concurrent.Semaphore。

概念

信号量(Semaphores)机制是一种卓有成效的进程同步工具,由荷兰学者Dijkstra提出的(这哥们貌似还提出过图的最短路径算法)。

信号量的值仅能由PV操作来改变。
p操作(wait):申请一个单位资源,进程进入。
v操作(signal):释放一个单位资源,进程出来。
一般来说,信号量S≥0时,S表示可用资源的数量。执行一次P操作意味着请求分配一个单位资源,因此S的值减1;当S<0时,表示已经没有可用资源,请求者必须等待别的进程释放该类资源,它才能运行下去。而执行一个V操作意味着释放一个单位资源,因此S的值加1;若S≤0,表示有某些进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行下去。
通俗来说就是信号量会有一个初始值S(S≥0),这时有线程申请资源,S就会减1(S--);当S==0时,再来申请资源的线程就会阻塞;已申请到资源的进程执行完毕后,会释放资源,S会加1(S++);此时S>0,阻塞的线程就会获取到资源,获取到资源的同时,S会进行减1操作。也就是说,最多只能有S个线程同时占用资源。

Java中的Semaphore

先上一张Semaphore的方法图


Semaphore
Semaphore

由图可以看出在构造方法里会传入一个int类型的参数,

/**
 * Creates a {@code Semaphore} with the given number of
 * permits and nonfair fairness setting.
 *
 * @param permits the initial number of permits available.
 *        This value may be negative, in which case releases
 *        must occur before any acquires will be granted.
 */
public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}

由注释可以看出该permits可以为负数,但是在这种情况下释放资源(releases)必须在申请资源(acquires)之前调用。

/**
 * Acquires a permit from this semaphore, blocking until one is
 * available, or the thread is {@linkplain Thread#interrupt interrupted}.
 * 从信号量中获得一个许可,线程阻塞直到获取到许可,或者线程被关闭
 * <p>Acquires a permit, if one is available and returns immediately,
 * reducing the number of available permits by one.
 * 如果有一个许可是可用的,就获取到该许可,并使可用许可数目减少1
 * <p>If no permit is available then the current thread becomes
 * disabled for thread scheduling purposes and lies dormant until
 * one of two things happens:
 * <ul>
 * <li>Some other thread invokes the {@link #release} method for this
 * semaphore and the current thread is next to be assigned a permit; or
 * <li>Some other thread {@linkplain Thread#interrupt interrupts}
 * the current thread.
 * </ul>
 *
 * <p>If the current thread:
 * <ul>
 * <li>has its interrupted status set on entry to this method; or
 * <li>is {@linkplain Thread#interrupt interrupted} while waiting
 * for a permit,
 * </ul>
 * then {@link InterruptedException} is thrown and the current thread's
 * interrupted status is cleared.
 *
 * @throws InterruptedException if the current thread is interrupted
 */
public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

/**
 * Releases a permit, returning it to the semaphore.
 * 释放一个许可,把它交给信号量
 * <p>Releases a permit, increasing the number of available permits by
 * one.  If any threads are trying to acquire a permit, then one is
 * selected and given the permit that was just released.  That thread
 * is (re)enabled for thread scheduling purposes.
 *
 * <p>There is no requirement that a thread that releases a permit must
 * have acquired that permit by calling {@link #acquire}.
 * Correct usage of a semaphore is established by programming convention
 * in the application.
 */
public void release() {
    sync.releaseShared(1);
}

那显而易见,releases()方法和acquires()方法就分别执行释放资源和申请资源的操作了。

代码

下面来用代码来说明信号量(Semaphore)在Java中的使用

public static void main(String[] args) throws InterruptedException {
    Semaphore semaphore = new Semaphore(3);
    ExecutorService executorService=Executors.newCachedThreadPool();
    for (int i=0;i<20;i++){
        executorService.execute(()->{
            try {
                semaphore.acquire();
                System.out.println(Thread.currentThread().getName()+" 进入!");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                semaphore.release();
                System.out.println(Thread.currentThread().getName()+" 释放!");
            }
        });
    }
    executorService.shutdown();
}

20个线程都在请求信号量,但信号量只允许有3个线程进入,所以只有获得信号量的线程结束释放信号量后才能有新的线程进入。如果我们不释放信号呢,就只能打印出3句log了。

pool-1-thread-1 进入!
pool-1-thread-5 进入!
pool-1-thread-2 进入!

控制台打印的结果和我们设想的一致。

应用

Semaphore可以用于流量控制,特别是对并发数有限制的场景。如数据库同时只允许有20个线程访问,就可以使用信号量来实现。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容