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

夜空中最亮的星 能否听清

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

夜空中最亮的星 能否记起

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

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

和会流泪的眼睛

给我再去相信的勇气

越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请指引我靠近你

夜空中最亮的星 是否知道

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

夜空中最亮的星 是否在意

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

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

也不愿忘记你的眼睛

哦 给我再去相信的勇气

哦 越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请照亮我向前行 哒~

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

和会流泪的眼睛 哦

给我再去相信的勇气

哦 越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请照亮我向前行

原子类、lock、synchronized简单性能对比

前言

前几天,我们分享了原子类相关的知识点,展示了原子类的一些用法,之前也分享了lock相关的应用,但是我一直有一个困惑,就是在多线程数据安全的几个常用解决方案中,到底哪一个性能最好,我们在实际应用开发中应该如何选择,今天我们就来简单探讨下这个问题。

性能对比

开始之前,我们先看这样一段代码:

public class CountDownLatchTest {
    static Integer count = 0;
    private static final int SIZE = 100;

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < SIZE; i++) {
            new Thread(new TaskPortion(startTime)).start();
        }
    }

    static class TaskPortion implements Runnable {
        private long startTime;
        public TaskPortion() {
        }
        public TaskPortion(long startTime) {
            this.startTime = startTime;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                System.out.println(count++);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

从代码大家应该能看出了,我原本是要分享CountDownLatch相关知识的,但是在实际操作的时候,我发现countDownLatch并不简单,至少我现在还对它了解不够,所以就临时换频道,分享各种多线程线程安全解决方案的性能测试。

今天我们主要测试三种解决方案的性能,分别是原子类、locksynchronized。从上面的代码中,我们也可以看出了,以上代码是线程不安全的,所以接下来我们就要分别通过这三种解决方案来优化上面的代码,然后分别测试运行时间。

lock

我们先看第一种lock,这里我们主要是应用可重入锁,然后优化代码:

public class CountDownLatchTest {
    static Integer count = 0;
    private static final int SIZE = 100;
    // 可重入锁
    private static final ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < SIZE; i++) {
            new Thread(new TaskPortion(startTime)).start();
        }
    }

    static class TaskPortion implements Runnable {
        private long startTime;
        public TaskPortion() {
        }
        public TaskPortion(long startTime) {
            this.startTime = startTime;
        }

        @Override
        public void run() {
            lock.lock();
            try {
                Thread.sleep(1000);
                System.out.println(count++);
                if (count == 99) {
                    System.out.println("用时:" + (System.currentTimeMillis() - startTime));
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}

我们定义了一个可重入锁,在run方法中加锁,然后在finally代码块中释放锁,运行以上代码,我们会得到如下运行结果:

整个运行过程特别慢,大概需要90秒,具体取决于电脑性能。

synchronized

然后我们再来看下synchronized加持下的运行性能,调整后的代码如下:

public class CountDownLatchTest {
    static Integer count = 0;
    private static final int SIZE = 100;

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < SIZE; i++) {
            new Thread(new TaskPortion(startTime)).start();
        }
    }

    static class TaskPortion implements Runnable {
        private long startTime;
        public TaskPortion() {
        }
        public TaskPortion(long startTime) {
            this.startTime = startTime;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                synchronized (count){
                    System.out.println(count++);
                    if (count == 99) {
                        System.out.println("用时:" + (System.currentTimeMillis() - startTime));
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

关于synchrronized这里就不过多说明了,应该算特别基础的知识,它能修饰变量、方法、代码块,在应用层面也比较灵活。

运行结果很感人,时间直接比lock快了90倍,但是运行过程中出现了并发的情况(翻车了),因为i++操作不是原子的,所以单synchrioned并不能保证线程安全。继续往后看,后面有最终解决方案。

原子类

接下来,我们来看最后一种——原子类,代码调整如下:

public class CountDownLatchTest {
    static AtomicInteger count = new AtomicInteger(0);
    private static final int SIZE = 100;

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < SIZE; i++) {
            new Thread(new TaskPortion(startTime)).start();
        }
    }

    static class TaskPortion implements Runnable {
        private long startTime;
        public TaskPortion() {
        }
        public TaskPortion(long startTime) {
            this.startTime = startTime;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                System.out.println(count.getAndAdd(1));
                if (count.get() == 99) {
                    System.out.println("用时:" + (System.currentTimeMillis() - startTime));
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果如下,性能方面确实要比lock优秀的多,但是运行结果依然翻车,出现了线程安全问题。

然后再一次测试的机缘巧合之下,我发现把原子类和synchronized组合一下,就可以完美地解决这个问题,代码调整如下:

public class CountDownLatchTest {
    static AtomicInteger count = new AtomicInteger(0);
    private static final int SIZE = 100;

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < SIZE; i++) {
            new Thread(new TaskPortion(startTime)).start();
        }
    }

    static class TaskPortion implements Runnable {
        private long startTime;
        public TaskPortion() {
        }
        public TaskPortion(long startTime) {
            this.startTime = startTime;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                synchronized (count){
                    System.out.println(count.getAndAdd(1));
                    if (count.get() == 99) {
                        System.out.println("用时:" + (System.currentTimeMillis() - startTime));
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

然后运行结果就正常了,而且性能一点也不弱,可以吊打lock

总结

在以前的认知里,我一直觉得synchronized比显式的lock要重,但是通过今天的测试,我发现自己以前的认知是有问题的,synchronized要比显式的lock性能好的多,同时也意识到单独使用原子类或者synchronized都是存在线程安全问题的,所以在日常开发中,更多时候是需要把两者完美组合的。

在此之前,我一直以为原子类是可以单独使用的,但是踩了今天的坑才知道,就算你用了synchronized或者原子类线程安全问题依然存在。总之,凡事多实践多总结,共勉!

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