【Java 并发编程】线程池机制 ( 测试线程开销 | 启动线程分析 | 用户态 | 内核态 | 用户线程 | 内核线程 | 轻量级进程 )
文章目录
- 一、测试线程开销
- 1、正常测试
- 2、不创建线程
- 3、只创建不启动线程
- 4、只启动不等待执行完成
- 二、分析测试结果
- 1、启动线程分析
- 2、用户线程与内核线程
- 3、轻量级进程
- 4、验证 Java 线程类型
一、测试线程开销
线程池是线程的缓存 , 在 Java 高并发场景中 , 所有的异步操作 , 都可以使用线程池 ;
使用线程池时 , 不建议用在 " 执行耗时较长的操作 " 的业务场景中 ;
线程池机制 最重要的功能就是 复用线程 ; 线程的创建 , 销毁 , 都是要消耗资源的 , 如果频繁创建销毁线程 , 会消耗很多资源 ;
1、正常测试
下面开始测试一下线程创建的开销 :
在主线程中 , 启动 101010 万个线程 , 每个线程中累加 count
变量 ;
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() {@Overridepublic void run() {count ++;}});// 启动线程thread.start();// 等待线程执行完成thread.join();}// 记录程序执行结束时间long endTime = System.currentTimeMillis();// 打印消耗的时间System.out.println("耗时 : " + ( endTime - startTime ) + " ms , 最终 count = " + count);}
}
执行结果 : 101010 万个线程执行完毕消耗 10.99210.99210.992 秒 ;
2、不创建线程
注释掉线程相关代码 :
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() {@Overridepublic void run() {count ++;}});// 启动线程thread.start();// 等待线程执行完成thread.join();*/}// 记录程序执行结束时间long endTime = System.currentTimeMillis();// 打印消耗的时间System.out.println("耗时 : " + ( endTime - startTime ) + " ms , 最终 count = " + count);}
}
执行结果 : 111 ms 执行完毕 ; 说明耗时操作是在 for
循环中 ;
3、只创建不启动线程
注释掉线程启动代码 :
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() {@Overridepublic void run() {count ++;}});/*// 启动线程thread.start();// 等待线程执行完成thread.join();*/}// 记录程序执行结束时间long endTime = System.currentTimeMillis();// 打印消耗的时间System.out.println("耗时 : " + ( endTime - startTime ) + " ms , 最终 count = " + count);}
}
执行结果 : 耗时 797979 ms , 也很快 , 大部分时间都在 启动 与 等待线程执行完毕消耗 ;
4、只启动不等待执行完成
注释掉等待线程执行完成代码 :
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() {@Overridepublic void run() {count ++;}});// 启动线程thread.start();// 等待线程执行完成//thread.join();}// 记录程序执行结束时间long endTime = System.currentTimeMillis();// 打印消耗的时间System.out.println("耗时 : " + ( endTime - startTime ) + " ms , 最终 count = " + count);}
}
执行结果 : 耗时 3.8663.8663.866 秒 ;
二、分析测试结果
1、启动线程分析
在上述测试中 , 如果只是创建 101010 万个 Thread
对象 , 这些在 Java 中就是普通的对象 ;
但是如果调用了 Thread
对象的 start()
方法 , 就要涉及到系统的线程切换 , 这个操作非常耗时 ;
操作系统的空间 , 分为 用户空间 和 内核空间 ;
用户空间中 , 有多个进程 , 每个进程有多个线程 , 每个进程都有一个 线程表 , 用于保存该进程中的线程 ;
JVM 创建的线程是 内核线程 ;
执行 main
函数时 , 处于 用户态 , 一旦调用了 start()
方法启动了线程 , 此时就进入了 内核态 , 该状态切换消耗巨大 ;
2、用户线程与内核线程
系统的线程分为 用户线程 和 内核线程 ;
用户线程 : 用户线程是 用户程序实现的线程 , 并负责管理线程的 创建 , 执行 , 调度 , 同步 ;
- 线程阻塞时 , 进程也会阻塞 ;
( Java 没有用到用户线程 )
内核线程 : 内核线程是 由内核管理的线程 , 其内部保存了线程的状态信息 , 上下文信息 , 如果频繁的切换线程 , 需要反复处理状态信息 , 上下文信息 , 会浪费很多资源 ;
- 线程阻塞时 , 进程不会阻塞 ;
- 内核线程效率比用户线程低 , 比进程高 ;
3、轻量级进程
轻量级进程 : 在我们写的程序中 , 虽然使用了内核线程 , 但 没有直接使用 , 而是 通过内核线程的高级接口使用内核线程 , 这个高级接口就是 " 轻量级进程 " , Java 程序中的 Thread
就是轻量级进程 , 每个 轻量级进程 都对应一个 内核线程 ;
4、验证 Java 线程类型
在任务管理器中可以查看线程数 :
执行下面的程序 : 创建了 100001000010000 个线程
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();}}
}
创建 100001000010000 线程后 , 发现线程数增加了 100001000010000 ;
由此可见 , Java 虚拟机创建的线程是内核线程 ;
Java 虚拟机创建线程 , 依赖于系统内核 , 内核空间的内核线程 与 用户空间的 Java 线程 是一一对应的关系 ;
【Java 并发编程】线程池机制 ( 测试线程开销 | 启动线程分析 | 用户态 | 内核态 | 用户线程 | 内核线程 | 轻量级进程 )相关推荐
- Java并发编程之锁机制之LockSupport工具
关于文章涉及到的jdk源码,这里把最新的jdk源码分享给大家----->jdk源码 前言 在上篇文章<Java并发编程之锁机制之AQS(AbstractQueuedSynchronizer ...
- 【Java 并发编程】线程池机制 ( ThreadPoolExecutor 线程池构造参数分析 | 核心线程数 | 最大线程数 | 非核心线程存活时间 | 任务阻塞队列 )
文章目录 前言 一.ThreadPoolExecutor 构造参数 二.newCachedThreadPool 参数分析 三.newFixedThreadPool 参数分析 四.newSingleTh ...
- 【Java 并发编程】线程池机制 ( 线程池示例 | newCachedThreadPool | newFixedThreadPool | newSingleThreadExecutor )
文章目录 前言 一.线程池示例 二.newCachedThreadPool 线程池示例 三.newFixedThreadPool 线程池示例 三.newSingleThreadExecutor 线程池 ...
- 高性能dhcp服务器,基于线程池机制的高性能DHCP服务器研究与实现
摘要: 随着互联网的蓬勃发展,IP地址资源越来越紧张.DHCP服务是在现有IPv4协议基础上解决IP地址资源短缺问题的有效途径. 目前,多数DHCP服务器是单线程运行,串行处理客户请求的.其应用于大型 ...
- dhcp计算机毕业论文,基于线程池机制的高性能DHCP服务器研究与实现-计算机科学与技术专业毕业论文.docx...
文档介绍: 西北丁业大学硕士学位论文 摘要摘 要随着互联网的蓬勃发展,IP地址资源越来越紧张.DHCP服务是在现有IPv4协议基础上解决IP地址资源短缺问题的有效途径.目前,多数DHCP服务器是单线程 ...
- Java并发编程—AQS原理分析
目录 一.AQS原理简述 二.自定义独占锁及共享锁 三.锁的可重入性 四.锁的公平性 五.惊群效应 AQS全称AbstractQueuedSynchronizer,它是实现 JCU包中几乎所有的锁.多 ...
- Java并发编程:Thread类的使用
为什么80%的码农都做不了架构师?>>> Java并发编程:Thread类的使用 在前面2篇文章分别讲到了线程和进程的由来.以及如何在Java中怎么创建线程和进程.今天我们来学 ...
- 【Java 并发编程】CountDownLatch 使用场景示例
文章目录 I CountDownLatch 使用场景举例 II CountDownLatch 简单线程阻塞示例 III CountDownLatch 多个线程联合阻塞示例 I CountDownLat ...
- 【Java 并发编程】线程池机制 ( 线程池执行任务细节分析 | 线程池执行 execute 源码分析 | 先创建核心线程 | 再放入阻塞队列 | 最后创建非核心线程 )
文章目录 一.线程池执行任务细节分析 二.线程池执行 execute 源码分析 一.线程池执行任务细节分析 线程池执行细节分析 : 核心线程数 101010 , 最大小成熟 202020 , 非核心线 ...
最新文章
- 根据Word表格自动生成SQL数据库脚本的VBScript代码
- 彻底弄懂C语言数组名
- ExtJs FormPanel布局
- python如何连接自己电脑服务器_Python远程连接windows服务器并上传数据
- 说说Java中原子性,可见性与指令重排序的理解
- UIKeyboard键盘相关知识点
- 经纬度坐标转换成px_经纬度坐标转像素坐标
- hibernate文档
- 数据结构上机实践第10周项目1 - 二叉树算法验证
- android mvp模式例子_[Android] MVP设计模式及实例
- setInterval.js
- 容器和泛型 容器重点掌握
- 阿里巴巴大数据实践:大数据建设方法论OneData
- 版本名称SNAPSHOT、alpha、beta、release、GA含义
- Vim文件和日历操作
- 小强源码分析系列-Spring Boot2-源码分析1---调试环境搭建
- 二代测序(Next generation sequencing)介绍
- Web前端人员如何提升能力 提高效率有哪些方法
- 【dbeaver】发生了错误。请参阅日志文件
- gmail通讯录同步