关于java:话说-线程切换线程数设置

46次阅读

共计 3589 个字符,预计需要花费 9 分钟才能阅读完成。

一、线程切换

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

简略流程是这样的:

  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();}



    // 单线程计算 sum
    private 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];
            }
        });
        // 第二个线程 计算一半到 length
        Thread 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.50063291E8
2 个线程计算 耗时:99 毫秒,计算结果:4.50063291E8
多个线程计算 耗时:162 毫秒,计算结果:4.50063291E8

能够看进去 2 个线程比一个执行快 然而 1000 个不肯定比 2 个执行快 因为 1000 个线程上下文切换就要破费很多资源

四、线程设置多少适合呢

  1. 实际中多少都是通过压测来确定的 然而压测初始值是能够本人推算一个的
  2. 怎么推算 比方我方才那个例子:我电脑 4 核 我想让每个核一个线程
    这样切换就少了 我把 ThreadNum 设置为 4
    输入后果:
一个线程计算 耗时:177 毫秒,计算结果:4.49997398E8
2 个线程计算 耗时: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

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

    java 最罕用的是 JProfiler (免费的) 本地开发测试

    如果曾经部署到服务器上了 能够用阿里开源的 Arthas 做统计

    后边会简略写一下:JProfiler Arthas 的应用

有问题能够留言哦,也能够公众号留言(回复快):

正文完
 0