多线程基础知识学习总结

线程的历史 —— 一部对CPU压榨的血泪史

1、单线程人工切换(纸带机)
CPU利用效率低很多时间是等着人去换各种纸带
2、多进程批处理(多个任务批量执行)
但是如果其中一个任务等待的话也会导致其他任务跟着等待
3、多进程并行处理(把程序写在不同的内存位置上来回切换)
4、多线程(一个程序内部不同任务的来回切换)
5、纤程/协程(绿色线程,用户管理的【而不是OS管理的】线程)

线程进程概念

在这里插入图片描述
首先看上面这个计算机的组成,
程序:
可执行文件。
进程:
首先双击我们电脑的一个程序比如QQ.exe,它会经过操作系统并把当前程序信息加载到内存中,内存中就有一个正在运行的QQ.exe,我们也可以再次点击QQ.exe登录另外的账号,所以一个程序是可以在内存里面放很多份的,在内存里面的每一份都可以称为一个进程。
进程就是操作系统分配资源的基本单位。(静态概念)

线程:
依然如上图,我们之前说点击QQ.exe,双击时我们的程序会进入到内存中变成一个进程,真正开始执行的时候程序是以线程为单位来执行的,操作系统会找到我们的主线程扔给CPU执行,然后如果主线程开启了其他的线程,CPU会在这些线程中来回切换。
概念上:线程就是CPU调度执行的基本单位(动态概念:多个线程共享同一个进程里的所有资源)
线程切换(底层角度理解):
在这里插入图片描述
ALU:计算单元
Registers:寄存器组(用于存放数据)
PC寄存器(存储到底执行到哪条指令了)
cache:缓存
所以当T1线程要运行的时候把T1的数据和指令放在CPU中,然后CPU计算单元对他进行计算,计算好了该做输出做输出,该做其他操作做其他操作。
但是根据操作系统调度算法的话,T1线程执行到一半把当前的CPU时间片用完了那么就会将T1的指令和数据放在缓存中,然后切换将T2的指令和数据放在CPU中继续做计算。如果T2的执行时间到了,就将T2的指令和数据放缓存里,再从缓存里面读取T1的数据和指令(当然这中间需要操作系统的调度切换【线程上下文切换】)。

单核CPU设定多线程是否有意义?

有意义,比方说我的某一个在CPU执行的任务需要等待网络响应(等待的过程不消耗CPU资源),那么等待的这段时间如果设置了多线程的话,就可以切换到其他线程去执行任务,这样就可以充分利用CPU的资源。
CPU密集型:大量时间做计算。
CPU的IO密集型:大量时间在等待。

工作线程数是否设置的越大越好?

不是,这样导致CPU资源消耗在对线程的切换上面去了


public class Main {
    //===================================================
    private static double[] nums = new double[10_0000_000];
    private static Random r = new Random();
    private static DecimalFormat df = new DecimalFormat("0.00");

    static {
        for (int i = 0; i < nums.length; i++) {
            nums[i] = r.nextDouble();
        }
    }

    private static void m1() {
        long start = System.currentTimeMillis();

        double result = 0.0;
        for (int i = 0; i < nums.length; i++) {
            result += nums[i];
        }

        long end = System.currentTimeMillis();

        System.out.println("m1: " + (end - start) + " result = " + df.format(result));
    }

    //=======================================================
    static double result1 = 0.0, result2 = 0.0, result = 0.0;

    private static void m2() throws Exception {

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < nums.length / 2; i++) {
                result1 += nums[i];
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = nums.length / 2; i < nums.length; i++) {
                result2 += nums[i];
            }
        });

        long start = System.currentTimeMillis();
        t1.start();
        t2.start();
        t1.join();
        t2.join();

        result = result1 + result2;

        long end = System.currentTimeMillis();

        System.out.println("m2: " + (end - start) + " result = " + df.format(result));
    }

    //===================================================================

    private static void m3() throws Exception {

        final int threadCount = 10000;
        Thread[] threads = new Thread[threadCount];
        double[] results = new double[threadCount];
        final int segmentCount = nums.length / threadCount;
        CountDownLatch latch = new CountDownLatch(threadCount);

        for (int i = 0; i < threadCount; i++) {
            int m = i;

            threads[i] = new Thread(() -> {
                for (int j = m * segmentCount; j < (m + 1) * segmentCount && j < nums.length; j++) {
                    results[m] += nums[j];
                }
                latch.countDown();
            });

        }

        double resultM3 = 0.0;

        long start = System.currentTimeMillis();
        for (Thread t : threads) {
            t.start();
        }

        latch.await();
        for(int i=segmentCount*threadCount;i<nums.length;i++)
        {
            resultM3+=nums[i];
        }
        for (int i = 0; i < results.length; i++) {
            resultM3 += results[i];
        }


        long end = System.currentTimeMillis();

        System.out.println("m3: " + (end - start) + " result = " + df.format(resultM3));
    }

    public static void main(String[] args) throws Exception {
        m1();
        m2();
        m3();
    }
}

运行结果如下所示:
在这里插入图片描述
在m3方法中设置了10000个线程,但是它的执行时间竟然花了1000多ms,效率反而大大的降低了。

工作线程数(线程池数)设多少合适?如何做计算?

1、根据CPU的核数来设定到底需要多少个线程,还是以上面的例子为例首先通过 Runtime.getRuntime().availableProcessors() 方法获取本电脑到底有多少个核数,我这里的电脑核心数为6,再将m3方法的threadcount设置为6,让每一个核心都进行计算得到的运行时间如下:

m1: 106 result = 50000558.33
m2: 60 result = 50000558.33
m3: 48 result = 50000558.33

当然如上看不了太明显的差异,可以把数值继续往上调则能看到更明显的差距。
但是在程序运行的过程中,在一台机器上并不是仅仅是跑我们的程序,还有很多其他的程序在运行。所以上面设置6并不一定代表能充分运行上所有的线程,得看实际运行的情况。一般程序中给CPU的余量在80%左右。

线程数量设定公式

N(threads) = N(CPU)U(CPU)(1+W/C)
1、N(CPU)是处理器的核的数目,可以通过Runtime.getRuntime().availableProcessors()得到
2、U(CPU)是期望的CPU利用率(介于0-1之间)
3、W/C是等待时间与计算时间的比率

W/C如何得到

1、用profiler做计算
2、Java常用的是Jprofiler
3、如果部署到了远程采用Arthes工具

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值