进程与线程基础概念

127 阅读4分钟

进程

程序由指令和数据组成,数据要读写,就必须将指令加载至CPU,数据加载至内存。进程要做的就是加载指令、管理IO的。 当一个程序被运行,从磁盘加载这个程序的代。码至内存,就开启了一个进程。进程是加载指令,管理内存,管理IO的。 进程之间相互独立。

线程

一个进程可以分为多个线程。一个线程就是一个指令流,将指令流以一定的顺序一条条交给cpu去执行。指令的运行会涉及到 磁盘 、 网络等设备。
线程是最小的调度单位,进程是资源分配的最小单位 , 在windows 中进程只是作为线程的容器,是不活动的

并行

在多核cpu 下,比如说两个线程,每个核都可以调度运行线程。这时候线程可以说是并行的。

并发 concurrent

时间片轮转,在单核cpu下,线程在围观表现上实际上还是串行化的,但是 时间片切换非常快,感觉是并行的。 线程轮流使用cpu

golang作者这样说

  • 并行是同一时间动手做多件事情的能力
  • 并发是同一时间应对多件事情的能力

生活例子

  • 并发 :家庭主妇做饭、喂奶、打扫卫生,一个人轮流交替多件事情
  • 并发且并行 : 雇了一个保姆,一起做以上三件事,既有并发、也有并行。一个人必会轮流做,也会产生共享资源的竞争,比如说一个人使用锅、另一个人只能等着,不能使用这口锅。
  • 并行:雇了2个保姆,3个人做三件事互不干扰,并行。

同步 和 异步

方法调用方面来讲:需要等待结果返回,才能继续运行,这就是同步
方法不需要等待结果返回。就能继续运行,这就是异步

使用多线程就可以做到,异步调用。

效率方面

单核下,多线程并不能实际提高运行效率,只是为了能够在不同任务之间进行切换,不同线程轮流使用cpu,不至于一个线程霸占cpu,其他线程没法干活。
多核下,任务的目的如果相同的话,将任务拆分,并行执行,可以提高程序的运行效率。
I/O 操作不占cpu、但是非常耗时,一般拷贝文件使用的是阻塞IO,这种线程虽然不用CPU,但需要一直等待IO结束,不能充分利用线程。 出现技术阻塞IO和异步IO

线程创建和运行

  • Thread
  • Runnable(可运行任务) 和 Thread(线程)

使用 Runable 更容易与线程池等高级API进行配合,Runable 让任务脱离了Thread 继承体系,更加灵活。

  • FutureTask:实现了 RunableFuture接口,RunableFuture 又实现了 Runable 接口和 Future 接口。 get方法 同步的等待。
public class FutureTask<V> implements RunnableFuture<V> 
public interface RunnableFuture<V> extends Runnable, Future<V>

FutureTask 构造方法

public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}

Callable 可以 抛出异常,且有返回值。

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

配合 Thread 使用 因为继承关系,实现了 Runable 接口,可以构造一个线程。

线程运行原理

zhuanlan.zhihu.com/p/574278435

每个线程都有自己独立的虚拟机栈(线程私有),在执行一个方法时,会同步在虚拟机栈中建立一个栈帧,方法运行完成后栈帧弹出,执行栈帧返回地址的下一行代码,每个线程只有一个活动栈帧,线程之间的栈帧互不干扰。

image.png image.png

线程上下文切换 (频繁发生影响性能)

当一个线程的时间片用完后或者其他自身原因被迫暂停运行了,这时候,另外一个线程或者、进程或者其他进程的线程就会被操作系统选中,用来占用处理器。这种一个线程被暂停,一个线程包选中开始执行的过程就叫做上下文切换。 原因:

  • 非自发性
    • cpu 时间片用完
    • 垃圾回收 stop the world
    • 更高优先级的线程要运行
  • 自发性
    • 调用 sleep 、 wait 、 join、park、lock 等方法。
    • synchronized 关键字。

当上下文切换发生时 ,需要由操作系统 保存当前线程的状态(每个栈帧得信息),并恢复另一个线程的状态,Java中对应得是程序计数器得工作(记住下一条指令得地址)。

所以 线程数超过 cpu 核心数就会引发线程上下文切换,影响 程序性能。