线程与进程基础

文章目录

  • 前言
  • [一、 线程与进程](#一、 线程与进程)
    • [1.1 什么是线程与进程?](#1.1 什么是线程与进程?)
    • [1.2 并发与并行](#1.2 并发与并行)
    • [1.3 同步调用与异步调用](#1.3 同步调用与异步调用)
    • [1.4 为什么要使用多线程?](#1.4 为什么要使用多线程?)

前言


在学习juc前,需要先对进程和线程之间整体有一个认知。我们之前或多或少接触过,一些特别高大上的概念,比如多线程,高并发 之类的。该文就是先对这些概念整体理清,为后续学习扫清障碍。


一、 线程与进程

1.1 什么是线程与进程?

进程

  • 进程是用来存放数据与指令等运行程序的组成部分,然后指令运行的时候还需用到磁盘、IO设备,网络等,其中进程就是用来加载指令,管理内存·,管理IO的。

  • 程序运行的时候,从磁盘 -> 内存就代表一个进程开启了

  • 进程可以看作一个程序的示例,比如我们打开任务管理器:

这表明,对于应用来说,可以运行一个或者多个进程。

线程

  • 一个进程包括多个线程
  • 一个线程就是一个指令流,将指令流的一条条指令以一定顺序交给CPU执行
  • Java中,线程是最小的调度单位,进程作为资源分配的最小单位。windows中,进程不活动,只是作为线程的容器。

二者对比 :

  • 进程拥有共享的资源,比如内存空间的,供子集线程共享
  • 进程间的通信较为复杂
    • 同一台计算机的进程通信称为IPC,比如套接字、消息队列等。
    • 不同计算机的通信要通过网路,并遵守共同的协议,比如http等。
  • 线程通信比较简单,因为它们可以共享进程内的内存,一个例子是多个线程访问一个共享变量。
  • 线程更轻量级,上下文切换成本较低。

1.2 并发与并行


  • 并发:两个及两个以上的任务在同一时间段执行
  • 并行:两个及两个以上的任务在同一时刻执行

并发

​ 在单核cpu中,线程实际是串行执行 的。操作系统中的组件,叫做任务调度器,将cpu的时间片(windows下接近15ms)分给不同的线程使用,只是由于cpu在线程间的切换非常快,导致体感上是同时运行的

​ 总结一句话:微观串行,宏观并行,但是,实际上还是串行。

并行

​ 在多核cpu中,每个核心可以处理一个线程,这样就可以真正意义上实现并行,即同一时刻,同时运行多个线程。

如图:

1.3 同步调用与异步调用

注意

代码中的Constants.MP4_FULL_PATH就理解为一个文件即可。

该代码作用就是花时间读取一个文件。

  • 同步调用
  • 异步调用

从运行结果,可以看出来,异步调用是通过一个Thread-0的线程执行的。

从上面的运行结果可以得到两者的含义。


同步调用:发出一个调用之后,结果没有返回的话,该调用就不可以返回,一直等待。

异步调用:调用在发出后,不用等待返回结果,调用直接返回。


其实简单来说:
异步调用 就是在main线程里面新开一个线程,并且新开的线程执行与否对于主线程接下来往下执行没有影响。
同步调用就是一直只执行当前线程呗,不开其它的线程。

根据这个,我们就可以引出下面的多线程:

  • 多线程的作用

​ 多线程可以让方法执行变成异步的(即不要干等着)。比如读取磁盘文件时,需要5秒,那如果我们不使用多线程,这5秒什么都做不了只能干等着。

1.4 为什么要使用多线程?

  • 在单核时代,使用多线程可以提高cpu和io系统的运行效率,如果只运行了一个java进程的情况下,当线程进行读取io的操作,此时线程被阻塞。如果没有多线程,那么此时整个进程都被阻塞, 无法使用cpu,系统整体效率只有50%。但是如果使用多线程的话,一个线程阻塞,其它线程还可以继续访问,大大提高系统运行效率。但是需要理解的是,单核中,多线程并不起到减少时间的作用。(单核只作了解)

  • 在多核时代。多线程主要是为了提高进程利用多核cpu的能力。

eg : 处理一个复杂的任务,如果只有一个线程,那么只有一个核心被利用到。失去了多核的作用,但是多个线程就可以同时使用多个核心同时处理任务,提高了解决任务的效率,时间约等于(单核时执行的时间 / cpu核心数)。

实例:

java 复制代码
@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();
}

运行结果:

上述代码含义就是 :

4个线程(t1、t2、t3、t4),每个线程调用方法执行2500万次,打印信息中是c开头的。

d开头方法是一个方法调用1亿次。

c为异步调用,d为同步调用,从结果来看,c方法的效率显著高于d方法(Score越小,效率越高)。

但是如果是在单核cpu中的话,不会出现性能的提升,因为单核的本质上还是串行,并不会产生并行的效果。

相关推荐
大模型玩家七七10 小时前
基于语义切分 vs 基于结构切分的实际差异
java·开发语言·数据库·安全·batch
寻星探路15 小时前
【深度长文】万字攻克网络原理:从 HTTP 报文解构到 HTTPS 终极加密逻辑
java·开发语言·网络·python·http·ai·https
曹牧17 小时前
Spring Boot:如何测试Java Controller中的POST请求?
java·开发语言
爬山算法18 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
kfyty72518 小时前
集成 spring-ai 2.x 实践中遇到的一些问题及解决方案
java·人工智能·spring-ai
猫头虎18 小时前
如何排查并解决项目启动时报错Error encountered while processing: java.io.IOException: closed 的问题
java·开发语言·jvm·spring boot·python·开源·maven
李少兄18 小时前
在 IntelliJ IDEA 中修改 Git 远程仓库地址
java·git·intellij-idea
忆~遂愿18 小时前
ops-cv 算子库深度解析:面向视觉任务的硬件优化与数据布局(NCHW/NHWC)策略
java·大数据·linux·人工智能
小韩学长yyds19 小时前
Java序列化避坑指南:明确这4种场景,再也不盲目实现Serializable
java·序列化