1. 1 不可撤销
  2. 2 小年兽 程嘉敏
  3. 3 手放开 李圣杰
  4. 4 迷人的危险3(翻自 dance flow) FAFA
  5. 5 山楂树之恋 程佳佳
  6. 6 summertime cinnamons / evening cinema
  7. 7 不谓侠(Cover 萧忆情Alex) CRITTY
  8. 8 神武醉相思(翻自 优我女团) 双笙
  9. 9 空山新雨后 音阙诗听 / 锦零
  10. 10 Wonderful U (Demo Version) AGA
  11. 11 广寒宫 丸子呦
  12. 12 陪我看日出 回音哥
  13. 13 春夏秋冬的你 王宇良
  14. 14 世界が终わるまでは… WANDS
  15. 15 多想在平庸的生活拥抱你 隔壁老樊
  16. 16 千禧 徐秉龙
  17. 17 我的一个道姑朋友 双笙
  18. 18 大鱼  (Cover 周深) 双笙
  19. 19 霜雪千年(Cover 洛天依 / 乐正绫) 双笙 / 封茗囧菌
  20. 20 云烟成雨(翻自 房东的猫) 周玥
  21. 21 情深深雨濛濛 杨胖雨
  22. 22 Five Hundred Miles Justin Timberlake / Carey Mulligan / Stark Sands
  23. 23 斑马斑马 房东的猫
  24. 24 See You Again Wiz Khalifa / Charlie Puth
  25. 25 Faded Alan Walker / Iselin Solheim
  26. 26 Natural J.Fla
  27. 27 New Soul Vox Angeli
  28. 28 ハレハレヤ(朗朗晴天)(翻自 v flower) 猫瑾
  29. 29 像鱼 王贰浪
  30. 30 Bye Bye Bye Lovestoned
  31. 31 Blame You 眠 / Lopu$
  32. 32 Believer J.Fla
  33. 33 书信 戴羽彤
  34. 34 柴 鱼 の c a l l i n g【已售】 幸子小姐拜托了
  35. 35 夜空中最亮的星(翻自 逃跑计划) 戴羽彤
  36. 36 慢慢喜欢你 LIve版(翻自 莫文蔚) 戴羽彤
  37. 37 病变(翻自 cubi) 戴羽彤
  38. 38 那女孩对我说 (完整版) Uu
  39. 39 绿色 陈雪凝
  40. 40 月牙湾 LIve版(翻自 F.I.R.) 戴羽彤
夜空中最亮的星(翻自 逃跑计划) - 戴羽彤
00:00 / 04:10

夜空中最亮的星 能否听清

那仰望的人 心底的孤独和叹息

夜空中最亮的星 能否记起

那曾与我同行 消失在风里的身影

我祈祷拥有一颗透明的心灵

和会流泪的眼睛

给我再去相信的勇气

越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请指引我靠近你

夜空中最亮的星 是否知道

那曾与我同行的身影 如今在哪里

夜空中最亮的星 是否在意

是等太阳先升起 还是意外先来临

我宁愿所有痛苦都留在心底

也不愿忘记你的眼睛

哦 给我再去相信的勇气

哦 越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请照亮我向前行 哒~

我祈祷拥有一颗透明的心灵

和会流泪的眼睛 哦

给我再去相信的勇气

哦 越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请照亮我向前行

多线程之Semaphore

前言

最近一段时间,我们一直都是在分享多线程相关的知识点,截止到今天我们已经分享过锁、计数器等相关知识,主要分享了一些常用的多线程控制方式,今天我们来继续分享另一个多线程控制组件——Semaphore

Semaphore

示例代码

Semaphore也是jdk1.5引入的组件,它的字面意思是信号量,但是单从字面翻译我们是无法得知它的作用的,根据官方注释以及网上的一些解释,semaphore简单来说就是多线程运行控制器,它的主要作用就是控制统一时间并发的线程数量,从这一特性上看,它最适用的场景就是限流,下面我们通过一段简单的代码来说明它的用法:

public class Example {
    private static final int THREAD_SIZE = 30;
    private static final Semaphore s = new Semaphore(5);

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(THREAD_SIZE);
        for (int i = 0; i < THREAD_SIZE*2; i++) {
            int finalI = i;
            executorService.execute(() -> {
                try {
                    s.acquire();
                    System.out.println(Thread.currentThread().getName() + " currentTimeMillis: "+ System.currentTimeMillis());
                    Thread.sleep(2000);
                    System.out.println("thread count: " + finalI);
                    s.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown();
    }
}

简单说明

在上面的代码中,我们定义了一个线程池,线程池大小为30,同时我们还定义了一个线程运行控制器,控制器的大小我们设定为5,也就是说,在当前控制器的作用下,同一时间只允许5个线程运行;

在线程内部,在线程运行最开始,我们通过Semaphoreacquire方法获取运行许可(拿不到运行凭证是无法运行的),你也可以通过tryAcquire方法获取运行凭证,两个方法的区别是tryAcquire有一个布尔的返回值,是非阻塞的,而acquire是阻塞的,如果拿不到会一直等,关于这一点,官方文档说的很清楚:

线程内部我们休眠了两秒,然后通过release方法释放Semaphore资源,这样其他的线程才能拿到这个凭证。

最后,我们用一个for循环来运行线程,循环次数我们设定为线程池大小的两倍,然后我们运行上面的代码,运行结果大致如下:

从运行结果中我们可以看到,虽然线程池大小是30,但是同一时间运行的线程只有5个,也就是我们Semaphore的初始化大小。我们可以试着把Semaphore的大小修改下看下运行结果:

初始化大小为10

同一时间有10个线程在运行

初始化大小为2

同一时间有2个线程在运行。

作用范围

下面我们把代码做一些简单调整:

我们分别在acquire方法前和release方法后加一行代码,这时候我们的semaphore初始化还是2,然后运行下:

我们发现,虽然acquire方法前和release方法之间以及之后的代码虽然同一时间只有两个线程在运行,但是之前的代码同一时间是有多于两个线程在运行的,这就是说Semaphore只会影响acquire方法前和release方法之间和之后区域的线程并发数,影响之后的代码是因为acquire方法是阻塞的,如果我们换成tryAcquire应该就是另外一番场景了:

然后运行:

根据运行结果我们发现,这时候acquire方法前和release方法之间以及之后的代码都不受限制了,都出现了并发数超过限制数的情况。具体原因,我们前面说了,tryAcquire方法是非阻塞的,所以这时候我们需要人为根据tryAcquire来控制代码逻辑,比如直接结束或者进行其他操作:

 boolean b = s.tryAcquire();
if (!b) {
    // doSomething()
    System.out.println("未拿到访问权限");
    return;
}

总结

如果说我们之前讲的锁,是单线程锁(同一时间只运行一个线程访问),那么Semaphore更像是一个多线程锁(同一时间允许多个指定线程访问),它也很像我们之前说的令牌桶(限流解决方案),凭令牌数据访问相关服务,与之不同的是,这里的Semaphore可以重复使用的,所以它同样可以用于限流,比如获取数据库连接,我们可以通过Semaphore来限制连接数。

好了,今天的内容就到这里,更多其他的应用场景就需要各位小伙伴根据自己的需求积极探索,大胆尝试了。

posted @ 2021-07-15 13:31  云中志  阅读(178)  评论(0编辑  收藏  举报