当前位置: 首页 > 图灵资讯 > 技术篇> 2.进程与线程

2.进程与线程

来源:图灵教育
时间:2023-06-05 09:35:33

2.1 进程与线程

进程:

  • 该程序由指令和数据组成。如果指令需要执行,数据需要读写,则需要将指令加载到cpu,数据加载到内存。该过程用于加载指令和管理IO、管理内存的
  • 当一个程序从磁盘加载代码到内存执行时,就会打开一个过程
  • 程序可视为程序的例子,大多数程序可以同时操作多个例子。

线程:

  • 一个过程有一个或多个线程
  • 线程是指令流,将指令流中的指令交给cpu执行
  • 作为资源分配的最小单位,线程是java调度的最小单位。

两者对比:

  • 过程基本上是独立的,线程存在于过程中,是过程的子集
  • 该过程拥有内存空间等共享资源,供内部线程共享
  • 过程之间的通信比线程更复杂
  • 同一台计算机不同过程之间的通信称为IPC
  • HTTPP等不同计算机之间的通信需要遵守相同的网络协议
  • 由于共享过程中的内存,线程通信相对简单
  • 线程较轻,线程上下文切换成本低于过程上下文切换
2.2 并行与并发

并发:

单核cpu同时执行多个线程

2.进程与线程_多核

并行:

多核cpu同时执行多个线程

2.进程与线程_i++_02

2.3 应用程序异步调用(案例1)

从调用的角度来看,如果

  • 不需要等待结果返回,执行异步

同步等待

/** * 同步等待 * @author xc * @date 2023/4/30 15:28 */@Slf4jpublic class Sync2_3 {    public static void main(String[] args) {        FileReader.read("D:学习\随便玩\wxPush\\DEPLOY.md");        log.debug("do other things ...");    }}

你可以看到,在执行其他事情之前,你需要文件才能完成

2.进程与线程_java_03

  • 在执行同步之前,需要等待结果返回

不要等异步

/** * 异步不等待 * @author xc * @date 2023/4/30 15:35 */@Slf4jpublic class Async2_3 {    public static void main(String[] args) {        new Thread(()-> FileReader.read("D:学习\随便玩\wxPush\\DEPLOY.md")).start();        log.debug("do other things ...");    }}

你可以看到,你可以做其他事情而不等待文件读完。

2.进程与线程_i++_04

1)设计

多线程可以使方法变为异步。例如,如果读取磁盘文件需要30分钟,如果没有线程调度机制,CPU在这30分钟内什么都做不了,其他代码将被暂停

2)结论

  • 例如,在项目中,视频文件需要更多的时间来转换格式,这是为了打开一个新的线程来处理视频转换,以避免阻塞主线程
  • tomcat的异步servlet也是类似的目的,使用户的线程处理耗时,避免阻塞tomcat工作线程
  • 在ui程序中,打开新的线程进行其他操作,以避免阻塞ui线程
提高应用效率(案例1)

充分利用多核cpu的优势,提高运行效率

计算1花费 10 计算ms2的费用 11 计算ms3的费用 9 ms汇总费用 1 ms

  • 如果时间串行需要31,需要31 ms
  • 如果时间平行,需要3次计算中最长的时间+1ms,即12ms

设计代码

package com.itcast;import org.openjdk.jmh.annotations.*;import java.util.Arrays;import java.util.concurrent.FutureTask;@Fork(1)@BenchmarkMode(Mode.AverageTime)@Warmup(iterations=3)@Measurement(iterations=5)public class MyBenchmark {    static int[] ARRAY = new int[1000_000_00];    static {        Arrays.fill(ARRAY, 1);    }    @Benchmark    public int c() throws Exception {        int[] array = ARRAY;        FutureTask<Integer> t1 = new FutureTask<>(()->{            int sum = 0;            for(int i = 0; i < 250_000_00;i++) {                sum += array[0+i];            }            return sum;        });        FutureTask<Integer> t2 = new FutureTask<>(()->{            int sum = 0;            for(int i = 0; i < 250_000_00;i++) {                sum += array[250_000_00+i];            }            return sum;        });        FutureTask<Integer> t3 = new FutureTask<>(()->{            int sum = 0;            for(int i = 0; i < 250_000_00;i++) {                sum += array[500_000_00+i];            }            return sum;        });        FutureTask<Integer> t4 = new FutureTask<>(()->{            int sum = 0;            for(int i = 0; i < 250_000_00;i++) {                sum += array[750_000_00+i];            }            return sum;        });        new Thread(t1).start();        new Thread(t2).start();        new Thread(t3).start();        new Thread(t4).start();        return t1.get() + t2.get() + t3.get()+ t4.get();    }    @Benchmark    public int d() throws Exception {        int[] array = ARRAY;        FutureTask<Integer> t1 = new FutureTask<>(()->{            int sum = 0;            for(int i = 0; i < 1000_000_00;i++) {                sum += array[0+i];            }            return sum;        });        new Thread(t1).start();        return t1.get();    }}

可以看出,与串行性能相比,并行性能有了很大的提高

2.进程与线程_java_05

总结

  • 在单核cpu下,多线程不能实际提高程序的运行效率,只是为了在不同的任务之间切换,不同的线程轮流使用cpu,以免一个线程总是占用cpu,其他线程不能工作
  • 多核cpu可以并行运行多个线程,但能否提高程序运行效率取决于情况
  • 有些任务,经过精心设计,并行分割和执行,当然可以提高程序的运行效率。但并非所有的计算任务都可以分割
  • 并非所有任务都需要拆分。如果角色的目的不同,谈论拆分和效率是没有意义的
  • IO操作不占用CPU,但我们复制使用阻塞IO,相当于不使用CPU需要等待IO结束,未能充分利用线程,然后有非阻塞IO和异步IO