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

夜空中最亮的星 能否听清

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

夜空中最亮的星 能否记起

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

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

和会流泪的眼睛

给我再去相信的勇气

越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请指引我靠近你

夜空中最亮的星 是否知道

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

夜空中最亮的星 是否在意

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

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

也不愿忘记你的眼睛

哦 给我再去相信的勇气

哦 越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请照亮我向前行 哒~

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

和会流泪的眼睛 哦

给我再去相信的勇气

哦 越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请照亮我向前行

多线程之读写锁

前言

java中,锁lock是多线程编程的一个重要组件,可以说凡是涉及到多线程编程,线程安全这一块就无法避开lock,进一步说就是所有的线程安全都是基于锁实现的,只是从形式上分为隐式锁和显式锁,synchronized就属于隐式锁,像我们之前分享的可重入锁就属于显式锁,当然显示锁还有很多,我们今天就来看一个很常用的显式锁——读写锁。

读写锁

读写锁是一对互斥锁,分为读锁和写锁。读锁和写锁互斥,让一个线程在进行读操作时,不允许其他线程的写操作,但是不影响其他线程的读操作;当一个线程在进行写操作时,不允许任何线程进行读操作或者写操作。

简单来说就是,写锁会排斥读和写,但是读锁只排斥写,这样的好处就很明显,在读多写少的应用场景下,比其他互斥锁性能要好很多。下面我们通过一段代码说明:

public class Example {
    private static final ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    private static AtomicInteger count = new AtomicInteger(0);

    public static void main(String[] args) {
        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        ExecutorService executorService = Executors.newCachedThreadPool();

        // 写操作1:写锁
        executorService.execute(() -> {
            writeLock.lock();
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("写count1: " + count.incrementAndGet());
            }
            writeLock.unlock();
        });
        // 读操作1:读锁
        executorService.execute(() -> {
            readLock.lock();
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("读count1: " + count.get());
            }
            readLock.unlock();
        });
        // 读操作2:读锁
        executorService.execute(() -> {
            readLock.lock();
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("读count2: " + count.get());
            }
            readLock.unlock();
        });
        // 写操作2:写锁
        executorService.execute(() -> {
            writeLock.lock();
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("写count2: " + count.incrementAndGet());
            }
            writeLock.unlock();
        });
        // 写操作3:写锁
        executorService.execute(() -> {
            writeLock.lock();
            for (int i = 0; i < 5; i++) {
                System.out.println("写count3: " + count.incrementAndGet());
            }
            writeLock.unlock();
        });

        executorService.shutdown();

    }
}

上面的代码中,我们分别有3次写操作,2次读操作,然后运行上面的代码:

运行结果图片上我们已经标注了线程休眠情况,根据运行结果虽然写count1休眠了1000,读count1休眠了500,但因为写锁的存在,排斥了其他读写操作,读只能等写锁释放,所以是先写后读,也就是写锁排斥读;

count2休眠了200,读count1休眠了500,虽然加了读锁,但结果还是读count2先运行,而且期间是读count1和读count2交替运行,说明读并不排斥读,而且读后面的写count2只休眠了100,但还是在读count1和读count2之后运行,说明读排斥写;

count2休眠100,写count3未休眠,但是写count3依然在写count2之后执行,说明写锁排斥写。

综上,我们在示例中分别证明了我们上面的结论:写锁会排斥读和写,但是读锁只排斥写。

总结

读写锁相比于其他互斥锁,优点很明显,也就是前面我们说的:在读多写少的应用场景下,比其他互斥锁性能要好很多。至于原因,我们也通过示例证明了:写锁会排斥读和写,但是读锁只排斥写。

关于读写锁,我觉得今天分享的内容已经比较详细,不仅展示了它的基本用法,同时还通过示例反证了它的互斥原则,所以如果你看明白了今天的内容,那么互斥锁这一款的知识你就掌握了。好了,今天的内容就到这里吧!

posted @ 2021-07-15 13:32  云中志  阅读(209)  评论(0)    收藏  举报