阅读 112

线程切换&线程数设置

一、 线程切换

在这里插入图片描述

线程的切换受操作系统的调度控制

简单流程是这样的:

  1. 操作系统让cpu执行线程1

  2. 线程1执行到指令003的时候 操作系统让cpu执行线程2

  3. cpu会将线程1的执行半成品放到缓存中

  4. cpu接着执行线程2

  5. cpu执行线程2的004指令的时候 操作系统又让cpu执行线程1

  6. cpu从缓存拿出线程1的残次品接着执行

  7. cpu就是一个无脑的计算中心 它不管调度 只管计算(像极了我们程序员)

  8. 而操作系统 是负责调度的 (像极了我们的领导)

  9. 在这里应该能发现一个点那就是:线程的切换是需要耗费操作系统资源的 (领导使唤你干活儿 肯定要浪费领导时间和精力)

二、 单核cpu设置多线程 是否有意义

一个cpu在一个时间段只能跑一个线程,我单个cpu设置多个线程有意义吗 ?

当然有意义了,你只有一个脑子(CPU),你除了想你女朋友(假如你有),你还会想的姑娘吗~~(会的)

为什么呢,因为有时候你女朋友不一定在消耗你的脑子 线程也是如此,比如一个线程01需要进行数据库数据加载,这时候它是不消耗cpu的,他是在消耗网络IO,CPU多值钱呀,肯定不能空闲,所以在线程01加载数据库数据的时候,就可以让CPU进行线程02的计算操作

CPU密集型:

CPU密集型也叫计算密集型,简单说就是大部分时候CPU使用在100% ,而读写磁盘/内存时间很短

比如大量数据计算的,进行数据分析的 ,也可能是CPU性能极差的而磁盘/内存性能很好的情况

IO密集型:

IO密集型跟CPU密集型相反,大部分时间CPU使用率很小,而读写磁盘/内存占用很大时间

比如读数据库比较多的,网络请求比较多的,也可能是磁盘/内存质量差的的情况

三、 线程数越大越好吗

当然不是了,线程数太大了,CPU就不干活了,只用来切换线程了

就比如程序员,一天内负责两个项目的开发 ,上午在公司9楼项目组,下午在公司6楼项目组,

这个还可以接受,如果开发100个呢 ? 9点在1楼,刚坐下就要去2楼,2楼刚坐下又要去三楼,这一天时间都用在换楼层了

写个小例子感受一下:

public class NumTest {// 一有1亿条数据的数组public static  double[] arr = new double[100000000];public static void main(String[] args) throws InterruptedException {// 初始化Random r = new Random();for (int i = 0; i < arr.length; i++) {arr[i] =r.nextInt(10);}// 单线程计算getSum01();// 2线程计算getSum02();// 多个线程计算getSum03();}// 单线程计算sumprivate static void getSum01() {long start = System.currentTimeMillis();double sum = 0;// 遍历求和for (int i = 0; i <arr.length ; i++) {sum = sum+arr[i];}long end = System.currentTimeMillis();// 输出日志System.out.println("一个线程计算 耗时:"+(end-start) + "毫秒,"  +"  计算结果:"+sum);}/**
     * 2个线程计算sum
     */static double sum01,sum02;private static void getSum02() throws InterruptedException {// 第一个线程 计算0到一半Thread thread01 = new Thread(() -> {for (int i = 0; i < arr.length / 2; i++) {sum01 = sum01 + arr[i];}});// 第二个线程 计算一半到lengthThread thread02 = new Thread(() -> {for (int i = arr.length / 2; i < arr.length; i++) {sum02 = sum02 + arr[i];}});long start = System.currentTimeMillis();thread01.start();thread02.start();// join 到主线程  阻塞thread01.join();thread02.join();// 执行完之后 求一下两个线程分别求和的和double  sum =sum01+sum02;long end = System.currentTimeMillis();// 打印日志System.out.println("2个线程计算 耗时:"+(end-start) + "毫秒,"  +"  计算结果:"+sum);}/**
     * 多个线程计算
     * @throws InterruptedException
     */private static void getSum03() throws InterruptedException {// 10个线程 可自行调节int ThreadNum = 1000;// 创建ThreadNum个长度的long数组 存储各个线程的求和结果double[] sums = new double[ThreadNum];// 存储多个线程Thread[]  threads = new Thread[ThreadNum];// 每个线程计算的数字个数final int segCount = arr.length/ThreadNum;CountDownLatch cdl = new CountDownLatch(ThreadNum);// 创建多个线程for (int i = 0; i < ThreadNum; i++) {int m = i;Thread thread = new Thread(() -> {// 每个线程执计算逻辑for (int j = segCount*m; j <segCount*(m+1) && j < arr.length; j++) {sums[m]+=arr[j];}cdl.countDown();});threads[i] = thread;}long start = System.currentTimeMillis();// 全部启动for (int i = 0; i < threads.length; i++) {threads[i].start();}cdl.await();// 最后求和double sum = 0;for (int i = 0; i < sums.length; i++) {sum+=sums[i];}long end = System.currentTimeMillis();// 打印日志System.out.println("多个线程计算 耗时:"+(end-start) + "毫秒,"  +"  计算结果:"+sum);}}输出结果:
一个线程计算 耗时:177毫秒,  计算结果:4.50063291E82个线程计算 耗时:99毫秒,  计算结果:4.50063291E8多个线程计算 耗时:162毫秒,  计算结果:4.50063291E8


可以看出来 2个线程比一个执行快 但是1000个不一定比2个执行快 因为1000个线程上下文切换就要花费很多资源

四、 线程设置多少合适呢

  1. 实践中多少都是通过压测来确定的 但是压测初始值是可以自己推算一个的

  2. 怎么推算 比如我刚才那个例子: 我电脑4核 我想让每个核一个线程
    这样切换就少了 我把ThreadNum设置为4
    输出结果:

一个线程计算 耗时:177毫秒,  计算结果:4.49997398E82个线程计算 耗时:99毫秒,  计算结果:4.49997398E8多个线程计算 耗时:79毫秒,  计算结果:4.49997398E8


  1. 那我们有多少核就设置多少个线程?
    不是的!
    首先,cpu不是只为你一个人服务的 除了你还有很多别的进程需要cpu
    一般说的充分利用CPU 不是说都用到100% 我们还要留一部分cpu进行利用,跟你上班一样,地铁30分钟+走路10分钟到公司,你如果9点上班 你会8点20出门吗 肯定会早一点出门 留点儿余地

  2. 线程数量 设定公式
    Nthread = Ncpu * Ucpu * (1+W/C )
    Ncpu : 处理器的核数 java可以这样获取:

     System.out.println(Runtime.getRuntime().availableProcessors());

    Ucpu: 期望CPU的利用率 在0 到 1 之间

    W/C: 是等待时间与计算时间的比率 wait/compute

    可以看出来:
    cpu合数 和 cpu使用率确定的情况下 等待时间(W)越长可以设置的线程数就应该越多

    如果c(计算时间)占比很大 也就是w/c ≈ 0 那合适的线程数就等于 NcpuxUcpux1

  3. 4中说的w和c 我怎么知道的 ?
    这些值呢,一般来说 通过工具(profiler)进行测算

    java最常用的是JProfiler (收费的) 本地开发测试

    如果已经部署到服务器上了 可以用阿里开源的 Arthas 做统计

    后边会简单写一下:JProfiler Arthas的使用



文章分类
后端
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐