基本概念见https://cloud.tencent.com/developer/article/1688297
进程是对运行时程序的封装,是系统进行资源调度和分配的的基本单位,实现了操作系统的并发;
线程是进程的子任务,是CPU调度和分派的基本单位,用于保证程序的实时性,实现进程内部的并发;线程是操作系统可识别的最小执行和调度单位。每个线程都独自占用一个虚拟处理器:独自的寄存器组,指令计数器和处理器状态。每个线程完成不同的任务,但是共享同一地址空间(也就是同样的动态内存,映射文件,目标代码等等),打开的文件队列和其他内核资源。
进程对应一个程序。每个进程对应一定的内存地址空间,并且只能使用它自己的内存空间,各个进程空间间互不干扰,并且进程保存了程序每个时刻的运行状态,这样就为进程切换提供了可能,当进程暂停时,它会保存进程当前的状态如进程的标识,进程的使用的资源,等重新切换回来时,便根据之前保存的状态进行恢复和执行。进程让操作系统的并发变成可能。对单核CPU而言,在宏观上看也有多个任务在同时运行,但事实上任一个时刻只有一个任务在占用CPU资源,这是因为CPU分配给单一任务执行的时间片很短,任务切换的频次高,造成多个任务并发执行的假象。
并发和并行的区别: 并行是指两个或者多个时间在同一时刻发生;并行是指两个或者多个时间在同一时间间隔内发生。 在单处理机系统中的多道程序环境下,并发性是指在一段时间内宏观上有多个程序在同时运行,微观上只是在分时交替执行。 并发是轮流处理多个任务,并行是同时处理多个任务。
进程的出现解决了操作系统的并发问题,但是随着电脑的普及,人们逐渐对实时性有了要求,因为一个进程在一个时刻只做一个任务,如果一个进程有多个任务,只能逐个地执行这些子任务,而这些子任务往往不存在顺序上的依赖,是可以并发执行的,既然CPU可以按照时间片的方式轮流切换来跑进程,那能不能给这些子任务打个标签,让CPU按照更细的时间片来执行子任务呢?这是可以的。由于子任务共享进程的内存等资源,因此隶属于同一个进程间的子任务的切换是不需要切换页目录以使用新的地址空间的。这样就为子任务间更快速的切换提供了可能,此时人们就发明了线程,让线程去执行一个子任务。线程让进程的内部并发成为可能。
进程是资源分配的最小单位,线程是CPU调度的最小单位。
所有以进程有关的资源,都被记录在PCB(进程控制块)中,以表示表示该进程拥有这些资源,或者正在使用他们。
进程是抢占处理机的调度单位,它拥有完整的虚拟内存地址空间,当进程发生调度时,不同的进程拥有不同的虚拟地址空间,而同一进程内的不同线程共享同一地址空间。线程与资源分配无关,线程属于某个进程,与进程内的线程共享资源。
**线程只由堆栈与寄存器,程序计数器和TCB(线程控制块)**组成。寄存器可被用来存储线程内的局部变量,但不能存储其他线程的相关变量。通常,一个进程中可以包含若干个线程,他们可以拥有进程所拥有的资源
线程不能看做独立应用,而进程可以看做独立应用。
进程有独立的地址空间,一个进程崩溃后,在保护模式下,不会对其他进程产生影响,而线程只是一个进程中不同的执行路径。
线程有自己的堆栈和局部变量(寄存器存储?),但线程没有自己独立的地址空间,一个线程挂掉相当于一个进程挂掉,所以多进程的程序比多线程的程序健壮。
进程的切换比线程的切换开销大。对一些要求同时进行,并且要求共享某些变量的并发操作,只能用线程不能用进程,每个独立的线程,有一个程序运行的入口,顺序执行序列,和程序的出口,但是线程不能独立执行,必须依存于某一个应用程序当中,由应用程序,提供对多个线程的执行控制。
备注:
Java 中,Thread.currentThread()可以获取到当前的线程。
Thread.currentThread().getName()可以获取到当前线程的名字。
一个程序是一个可执行的文件,而一个进程是一个执行中程序的实例。
既然说Java采用的是单线程编程模型,是不是意味着JVM虚拟机也是单线程的?要注意的是,虽然只有一个线程执行任务,并不代表JVM中只有一个线程,JVM实例在创建的时候同时会创建很多其他线程,比如GC是由一个垃圾收集器线程专门负责的,所以JVM是多线程的。
public class ThreadTest { private static void attack(){ System.out.println("Fight"); System.out.println("Current Thread is : "+Thread.currentThread().getName()); } public static void main(String[] args) { Thread t = new Thread(){ public void run(){ attack(); } }; System.out.println("Current Thread is :"+Thread.currentThread().getName()); t.run(); } }
直接调用线程中run(),函数run还是在当前线程(主线程执行),结果:
Current Thread is :main Fight Current Thread is : main
public class ThreadTest { private static void attack(){ System.out.println("Fight"); System.out.println("Current Thread is : "+Thread.currentThread().getName()); } public static void main(String[] args) { Thread t = new Thread(){ public void run(){ attack(); } }; System.out.println("Current Thread is :"+Thread.currentThread().getName()); t.start(); } }
调用start,执行attack()的线程是新的线程。
Current Thread is :main Fight Current Thread is : Thread-0
总结:调用run会沿用主线程执行方法,而用start()会用一个非main的线程执行这个方法。
Thread是一个类,runnable是一个接口。
class Thread implements Runnable{ }
public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }