使用多线程踩的“坑”——使用多线程真会使用CPU所有的内核吗?

学习多线程的时候,我们都知道如果多个线程分配到CPU多个内核是可以并发的执行。但真的是这样的吗?

先来看看电脑配置:

电脑配置

测试电脑是单CPU,4核。按道理来说创建4个线程应该可以分配到4个内核同时执行。接下来执行测试代码看结果!

public class ThreadTest {

    private static final int num = 1000 * 1000;

    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            for (int i = 0; i < num; i++) {
                System.out.println(i);
            }
        },"线程1").start();

        new Thread(()->{
            for (int i = 0; i < num; i++) {
                System.out.println(i);
            }
        },"线程2").start();

        new Thread(()->{
            for (int i = 0; i < num; i++) {
                System.out.println(i);
            }
        },"线程3").start();

        new Thread(()->{
            for (int i = 0; i < num; i++) {
                System.out.println(i);
            }
        },"线程4").start();
    }
}

测试代码创建了四个线程,四个线程都遍历一百万次。通过使用JDK自带监控工具:Visual VM 查看线程的执行过程,是不是真的如我想象,并发的执行线程呢?

Visual VM线程监控截图

关注红色框的内容,惊奇的发现,多个线程根本没有并发执行,而是不断的在线程之间上下文切换!也就是说,4个线程都是在单个内核执行,其他的内核并没有工作!

???

这就有点颠覆我的认知了,后来不断的google、查阅资料我才发现,这个与操作系统CPU的算法有关系!【参考文章

线程的调度是根据cpu的算法,如果线程的运算量不大,cpu算法调度线程不一定会平均分配给每个内核的。那意思是如果运算量大的话,就会使用到其他的内核咯?

继续改进测试代码:

public class ThreadTest{

    // 数据量
    private static final int num = 2000 * 1000;

    // 设置栅栏是为了防止子线程还没结束就执行main线程输出耗时时间
    private static final CountDownLatch countDownLatch = new CountDownLatch(4);

    private static ExecutorService service = Executors.newFixedThreadPool(4);

    private static final String filePath1 = "/Users/hao/IdeaProjects/Sample/src/test1.txt";
    private static final String filePath2 = "/Users/hao/IdeaProjects/Sample/src/test2.txt";
    private static final String filePath3 = "/Users/hao/IdeaProjects/Sample/src/test3.txt";
    private static final String filePath4 = "/Users/hao/IdeaProjects/Sample/src/test4.txt";

    private static File file1 = new File(filePath1);
    private static File file2 = new File(filePath2);
    private static File file3 = new File(filePath3);
    private static File file4 = new File(filePath4);

    public static void main(String[] args) throws InterruptedException, IOException {
        // 开始时间
        long startTime = System.currentTimeMillis();

        new Thread(new WriteFileThread(file1),"线程1").start();
        new Thread(new WriteFileThread(file2),"线程2").start();
        new Thread(new WriteFileThread(file3),"线程3").start();
        new Thread(new WriteFileThread(file4),"线程4").start();

        try {
            countDownLatch.await();
        } finally {
            service.shutdown();
        }

        // 结束时间
        long endTime = System.currentTimeMillis();
        System.out.println();
        System.out.println("总耗时间为:" + (endTime - startTime) / 1000.0 + "s");

    }

    static class WriteFileThread implements Runnable {

        private File file;

        public WriteFileThread(File file) {
            this.file = file;
        }

        @Override
        public void run() {
            writeFile(file);
        }
    }

    static void writeFile(File file){
        // 判断是否有该文件
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        long startTime = System.currentTimeMillis();
        //创建输出缓冲流对象
        BufferedWriter bufferedWriter = null;
        try {
            bufferedWriter = new BufferedWriter(new FileWriter(file));
        } catch (IOException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < num; i++) {
            try {
                bufferedWriter.write(i);
                bufferedWriter.newLine();
                bufferedWriter.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println(Thread.currentThread().getName() + "执行完成,耗时 : " + (endTime - startTime) / 1000 + "s");
        countDownLatch.countDown();
        try {
            bufferedWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

输出结果:

线程4执行完成,耗时 : 22s
线程3执行完成,耗时 : 22s
线程1执行完成,耗时 : 22s
线程2执行完成,耗时 : 24s

总耗时间为:24.709s

再查看Visual VM 监控工具,可以发现,4个线程都并发的执行了!

Visual VM 监控截图

总结:

通过这一个“坑”,使我认识到自己过去一些不良的学习习惯。虽然我了解不少的并发类、并发原理,而且也写了不少的demo实践,但是没想到底层执行却有可能与自己所识的相差甚远。我学习多线程的过程中并没有结合实际,没有试过看监控工具,没有试过调试JVM参数,没有试过压测,怎能证明这些并发工具类真的能优化系统性能呢?与此同时,也明白到自己哪些的不足,首先不熟悉操作系统的知识,这是一块硬伤,过去也常常因为不熟悉操作系统而进入牛角尖、理解不透知识,往后应好好的恶补这块内容,提高“内功”;另外一方面,自己在很多方面总是理所当然,书上、网上博客说什么就认为是什么,没有实践、没有结合自己的思考,这也是我现阶段比较缺乏的一种思维方式,往后也要改正、注意习惯!

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

推荐阅读更多精彩内容