前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Java 并发编程】线程池机制 ( 测试线程开销 | 启动线程分析 | 用户态 | 内核态 | 用户线程 | 内核线程 | 轻量级进程 )

【Java 并发编程】线程池机制 ( 测试线程开销 | 启动线程分析 | 用户态 | 内核态 | 用户线程 | 内核线程 | 轻量级进程 )

作者头像
韩曙亮
发布2023-03-29 16:53:27
1.3K0
发布2023-03-29 16:53:27
举报
文章被收录于专栏:韩曙亮的移动开发专栏

文章目录

一、测试线程开销


线程池是线程的缓存 , 在 Java 高并发场景中 , 所有的异步操作 , 都可以使用线程池 ;

使用线程池时 , 不建议用在 " 执行耗时较长的操作 " 的业务场景中 ;

线程池机制 最重要的功能就是 复用线程 ; 线程的创建 , 销毁 , 都是要消耗资源的 , 如果频繁创建销毁线程 , 会消耗很多资源 ;

1、正常测试

下面开始测试一下线程创建的开销 :

在主线程中 , 启动

10

万个线程 , 每个线程中累加 count 变量 ;

代码语言:javascript
复制
public class Main {

    /**
     * 线程中对该值进行累加操作
     */
    private static int count = 0;

    public static void main(String[] args) throws InterruptedException {
        // 记录程序开始执行时间
        long startTime = System.currentTimeMillis();

        // 创建 10 万个线程, 开启线程后, 向集合中添加一个元素
        for (int i = 0; i < 100000; i ++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    count ++;
                }
            });

            // 启动线程
            thread.start();
            // 等待线程执行完成
            thread.join();
        }

        // 记录程序执行结束时间
        long endTime = System.currentTimeMillis();
        // 打印消耗的时间
        System.out.println("耗时 : " + ( endTime - startTime ) + " ms , 最终 count = " + count);
    }
}

执行结果 :

10

万个线程执行完毕消耗

10.992

秒 ;

在这里插入图片描述
在这里插入图片描述

2、不创建线程

注释掉线程相关代码 :

代码语言:javascript
复制
public class Main {

    /**
     * 线程中对该值进行累加操作
     */
    private static int count = 0;

    public static void main(String[] args) throws InterruptedException {
        // 记录程序开始执行时间
        long startTime = System.currentTimeMillis();

        // 创建 10 万个线程, 开启线程后, 向集合中添加一个元素
        for (int i = 0; i < 100000; i ++) {
            /*Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    count ++;
                }
            });

            // 启动线程
            thread.start();
            // 等待线程执行完成
            thread.join();*/
        }

        // 记录程序执行结束时间
        long endTime = System.currentTimeMillis();
        // 打印消耗的时间
        System.out.println("耗时 : " + ( endTime - startTime ) + " ms , 最终 count = " + count);
    }
}

执行结果 :

1

ms 执行完毕 ; 说明耗时操作是在 for 循环中 ;

在这里插入图片描述
在这里插入图片描述

3、只创建不启动线程

注释掉线程启动代码 :

代码语言:javascript
复制
public class Main {

    /**
     * 线程中对该值进行累加操作
     */
    private static int count = 0;

    public static void main(String[] args) throws InterruptedException {
        // 记录程序开始执行时间
        long startTime = System.currentTimeMillis();

        // 创建 10 万个线程, 开启线程后, 向集合中添加一个元素
        for (int i = 0; i < 100000; i ++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    count ++;
                }
            });

            /*// 启动线程
            thread.start();
            // 等待线程执行完成
            thread.join();*/
        }

        // 记录程序执行结束时间
        long endTime = System.currentTimeMillis();
        // 打印消耗的时间
        System.out.println("耗时 : " + ( endTime - startTime ) + " ms , 最终 count = " + count);
    }
}

执行结果 : 耗时

79

ms , 也很快 , 大部分时间都在 启动 与 等待线程执行完毕消耗 ;

在这里插入图片描述
在这里插入图片描述

4、只启动不等待执行完成

注释掉等待线程执行完成代码 :

代码语言:javascript
复制
public class Main {

    /**
     * 线程中对该值进行累加操作
     */
    private static int count = 0;

    public static void main(String[] args) throws InterruptedException {
        // 记录程序开始执行时间
        long startTime = System.currentTimeMillis();

        // 创建 10 万个线程, 开启线程后, 向集合中添加一个元素
        for (int i = 0; i < 100000; i ++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    count ++;
                }
            });

            // 启动线程
            thread.start();
            // 等待线程执行完成
            //thread.join();
        }

        // 记录程序执行结束时间
        long endTime = System.currentTimeMillis();
        // 打印消耗的时间
        System.out.println("耗时 : " + ( endTime - startTime ) + " ms , 最终 count = " + count);
    }
}

执行结果 : 耗时

3.866

秒 ;

在这里插入图片描述
在这里插入图片描述

二、分析测试结果


1、启动线程分析

在上述测试中 , 如果只是创建

10

万个 Thread 对象 , 这些在 Java 中就是普通的对象 ;

但是如果调用了 Thread 对象的 start() 方法 , 就要涉及到系统的线程切换 , 这个操作非常耗时 ;

操作系统的空间 , 分为 用户空间 和 内核空间 ;

用户空间中 , 有多个进程 , 每个进程有多个线程 , 每个进程都有一个 线程表 , 用于保存该进程中的线程 ;

JVM 创建的线程是 内核线程 ;

执行 main 函数时 , 处于 用户态 , 一旦调用了 start() 方法启动了线程 , 此时就进入了 内核态 , 该状态切换消耗巨大 ;

2、用户线程与内核线程

系统的线程分为 用户线程 和 内核线程 ;

用户线程 : 用户线程是 用户程序实现的线程 , 并负责管理线程的 创建 , 执行 , 调度 , 同步 ;

  • 线程阻塞时 , 进程也会阻塞 ;

( Java 没有用到用户线程 )

内核线程 : 内核线程是 由内核管理的线程 , 其内部保存了线程的状态信息 , 上下文信息 , 如果频繁的切换线程 , 需要反复处理状态信息 , 上下文信息 , 会浪费很多资源 ;

  • 线程阻塞时 , 进程不会阻塞 ;
  • 内核线程效率比用户线程低 , 比进程高 ;
在这里插入图片描述
在这里插入图片描述

3、轻量级进程

轻量级进程 : 在我们写的程序中 , 虽然使用了内核线程 , 但 没有直接使用 , 而是 通过内核线程的高级接口使用内核线程 , 这个高级接口就是 " 轻量级进程 " , Java 程序中的 Thread 就是轻量级进程 , 每个 轻量级进程 都对应一个 内核线程 ;

在这里插入图片描述
在这里插入图片描述

4、验证 Java 线程类型

在任务管理器中可以查看线程数 :

在这里插入图片描述
在这里插入图片描述

执行下面的程序 : 创建了

10000

个线程

代码语言:javascript
复制
public class Test {
    public static void main(String[] args) {
        for (int i = 0; i < 10000; i ++){
            new Thread(()->{
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

创建

10000

线程后 , 发现线程数增加了

10000

;

由此可见 , Java 虚拟机创建的线程是内核线程 ;

在这里插入图片描述
在这里插入图片描述

Java 虚拟机创建线程 , 依赖于系统内核 , 内核空间的内核线程 与 用户空间的 Java 线程 是一一对应的关系 ;

在这里插入图片描述
在这里插入图片描述
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-09-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 一、测试线程开销
    • 1、正常测试
      • 2、不创建线程
        • 3、只创建不启动线程
          • 4、只启动不等待执行完成
          • 二、分析测试结果
            • 1、启动线程分析
              • 2、用户线程与内核线程
                • 3、轻量级进程
                  • 4、验证 Java 线程类型
                  相关产品与服务
                  腾讯云服务器利旧
                  云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档