大厂必问_什么是进程线程纤程

什么是进程?

从底层角度理解什么是进程

在这里插入图片描述
如上图是计算机底层的一张图。什么是程序?举个例子,QQ.exe是windows下很普通的可以运行的一个程序,那么怎么去运行它呢?一般来说我们去双击它,它就开始运行,操作系统找到这个可执行文件,所以所谓的程序就是操作系统可以执行的这么一个文件。找到这个文件后把相关信息load到内存中,也就是说在内存中有一个正在运行的QQ.exe。再双击一下内存中又有一个QQ.exe,一个程序可以在内存中放很多份,每一份都是一个进程。一个程序可以有多个进程,当然它可以通过代码来控制只有一个进程。进程是操作系统进行资源分配的基本单位。

什么是线程?

从通俗角度理解什么是线程

从通俗角度理解所谓进程就是一个程序的不同执行路径。如果程序中没有同时运行的执行路径,那它就是单线程,也称为主线程(main方法开启的那个线程)。当启动一个程序之后,它在中间会产生分支,同时有不同分支在同时运行,这个分支在等待数据的输入,这个分支在等待网络的输入等。这几个分支是在同一个时间段里同时运行,这就是多线程。

从底层角度理解什么是线程

依然回到本文最开始的那张图,一个程序QQ.exe,当双击它的时候,会进入内存中变成一个进程。那么怎么开始执行这个进程呢?真正开始执行的时候程序是以线程为单位开始执行的。操作系统会找到主线程(mian方法),然后扔给CPU执行,找到主线程时如果中间开启了其它线程,在线程之间来回切换。一个进程可能包含多个线程,但如果从概念角度来理解,进程是资源分配的基本单位,进程是一个静态概念,而线程是在进程的内部,是调度执行的基本单位。线程是一个动态概念,多个线程之间共享同一个进程里的所有资源。

什么是线程切换

在这里插入图片描述
作为程序来说,它有指令和数据,作为CPU来说,它有几个重要的组成单元(计算单元ALU,寄存器组Registers,寄存器PC)。T1线程执行的时候,把指令和数据放入CPU,然后CPU寄存单元对它进行计算,计算好后该做输出做输出,该做其它操作做其它操作。假如根据操作系统的调度算法,T1线程到时间了,如果要切换到T2线程,那么将T1的指令和数据的地址存到cache中,再把T2相关的指令和数据放入CPU继续来做计算,所以CPU特别傻,只管算,只根据这条指令计算,其它什么都不管。至于指令和数据属于哪个线程的,这是属于操作系统的事。假如T2时间也到了,想让T1继续回来执行。只需把T2放到cache中去,把原来T1缓存好的数据放回来。CPU又是进行傻傻的计算,中间是需要经过操作系统调度的,是需要消耗资源的。所以线程切换就是这么一个过程,专业名词context switch。

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

众所周知,一个CPU只能跑一个线程,既然只能跑一个线程,那么设定多线程有意义吗?当然有意义。
线程是一段小程序做的一些操作,这些操作并不是都消耗CPU的,比如执行到某一个点需要等待网络的一个输入,网上传过来一个数据,拿到这个数据才能继续往下执行的状态,实际上在这个等待过程(或者sleep)是不消耗CPU的,那么这个时候这段时间可以让给别的线程执行。这就充分利用了CPU的资源。

线程按照执行分类为CPU密集型和IO密集型,这类线程(CPU密集型)大量时间是做计算,比如for循环,加减乘除等。这类线程对CPU利用率比较高。另一类线程(IO密集型)大量时间用来等待输入和输出。做一些简单的消耗CPU资源的操作。

线程数是不是越大越好?

当然不是,线程切换是需要消耗资源的。看下面代码示例:

package 多线程与高并发;

import java.text.DecimalFormat;
import java.util.Random;
import java.util.concurrent.CountDownLatch;

/**
 * 描述     线程数是不是越大越好
 *
 * @author lixinzhen
 * @create 2022/3/16 1:50
 */
public class T01_MutliVSSingle_ContextSwitch {
    private static double[] nums = new double[1_0000_0000];
    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 InterruptedException {
        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 InterruptedException {
        final int theadCount = 10000;
        Thread[] threads = new Thread[theadCount];
        double[] results = new double[theadCount];
        final int segmentCount = nums.length/theadCount;
        CountDownLatch latch = new CountDownLatch(theadCount);
        for (int i = 0; i < theadCount; i++) {
            int m = i;
            threads[i] = new Thread(()->{
                if (m == theadCount-1){
                    for (int j = m*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 = 0; i < results.length; i++) {
            resultM3 += results[i];
        }
        long end = System.currentTimeMillis();
        System.out.println("m3: " + (end-start) + " result = " + df.format(result));
    }

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

运行结果:
在这里插入图片描述

工作线程数(线程池中的线程数)设多少最合适?

根据CPU的核数,CPY的能力来设置到底设多少个线程才合适。

 private static void m3() throws InterruptedException {
        final int theadCount = 32;
        Thread[] threads = new Thread[theadCount];
        double[] results = new double[theadCount];
        final int segmentCount = nums.length/theadCount;
        CountDownLatch latch = new CountDownLatch(theadCount);
        for (int i = 0; i < theadCount; i++) {
            int m = i;
            threads[i] = new Thread(()->{
                if (m == theadCount-1){
                    for (int j = m*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 = 0; i < results.length; i++) {
            resultM3 += results[i];
        }
        long end = System.currentTimeMillis();
        System.out.println("m3: " + (end-start) + " result = " + df.format(result));
    }

如图笔者本机电脑64G内存,32核,将上文代码中m3线程的数量设为32,运行结果如下:
在这里插入图片描述
当然并不是完全根据CPU的核数就可以确定设多少线程数才最合适。

在这台机器上并不是只有这些线程还有好多线程也在执行。所以线程设置为32是不是充分利用也不一定。

另外从服务安全的角度来讲,需要给CPU留点余量,如果CPU全被用完,此时再进来一些线程会有安全问题的。

对于计算多少线程数最合适有一套公式,如下图取自《Java并发编程实践》一 书中。
在这里插入图片描述

什么是纤程或协程?

什么是程序?

程序是可执行的文件,如上文提到的QQ.exe。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

༄༊心灵骇客༣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值