进程与线程
前言
进程可以看作是程序的执行过程。一个程序的运行需要CPU时间、内存空间、文件以及I/O等资源。操作系统就是以进程为单位来分配这些资源的,所以说进程是分配资源的基本单位。
进程是动态的,程序是静态的
程序是静态的,它本身作为一种软件资源可以长期保存在磁盘(常说的硬盘)中。比如QQ,QQ作为一个程序,其本身保存在计算机的磁盘上。此时,它并没有得到CPU、内存、I/O等资源。因此当前的QQ程序只是一个静态的程序并不能给我们实现视频、语音等功能。
但当QQ程序开始执行时,操作系统就会将QQ程序从磁盘中装入内存,同时也会在操作系统中创建属于QQ的进程,这些新创建的QQ进程会得到操作系统分配的CPU、内存、I/O等资源,得到这些资源后,创建出来的QQ进程就可以实现视频和语音的功能。而当我们点击退出QQ后,这些进程就会立刻消亡,分配得到的资源也会被释放。
由此可以看出,QQ进程是QQ程序的执行过程,它是动态的,有一定的生命周期,会动态的产生和消亡。进程是资源分配的单位。
程序与进程并不是一 一对应的关系虽然进程可以看作是程序的执行过程,但并非一个程序对应一个进程,即二者并不是一 一对应的关系。程序与进程的关系可能有以下几种:
一个程序产生一个进程:
比如Win10的记事本程序(notepad.exe),每打开一个txt文本文件,就只会启动一个记事本进程。
一个程序产生多个进程:
比如浏览器启动时,一般都会产生多个进程,这些进程相互配合,互相影响,共同实现浏览器的功能。
一个程序可以被多个进程共用:
比如一个记事本程序在执行时,就只会产生一个进程。但当我们再用记事本程序打开一个文件时,此时就会再次在操作系统中创建一个新的进程,这个新的进程同样也会调用记事本程序。即在此刻,计算机磁盘中只有一个记事本程序,但是操作系统中却有两个记事本进程在共用这个程序,且这两个进程互不影响。
一个进程又可能要用到多个程序:
比如,用C语言写了一个helloword.c的程序。此时,输入命令gcc helloword.c
。那么操作系统会创建一个进程,它调用c编译程序,对helloword.c文件进行编译。这个进程在执行编译的过程中,除了调用c编译程序和我们编写的helloword程序外,还会用到c预处理程序、连接程序、结果输出程序等。
而线程从属于进程,只能在进程的内部活动,多个线程共享进程所拥有的的资源。如果把进程看作是完成许多功能的任务的集合,那么线程就是集合中的一个任务元素,负责具体的功能。虽然CPU、内存、I/O等资源分配给了进程,但实际上真正利用这些资源并在CPU上执行的却是线程,即真正完成程序功能的是线程。
因为进程作为这些资源的拥有者,它的负载很重,在进程的创建、切换、删除过程中的时间和空间开销都很大。所以目前主流的操作系统都只将进程作为资源的拥有者,而把CPU调度和运行的属性赋予了线程。
比如打开浏览器程序,会产生相应的进程,浏览器进程中包含有许多线程,如HTTP请求线程,I/O线程,渲染线程,事件响应线程等。浏览器进程拥有着内存和I/O资源等,但当我们在浏览器中输入文字时,真正使用I/O资源接收我们输入的文字,并在CPU处理文字的却是浏览器进程中的I/O线程。即真正完成浏览器文字输入功能的是线程。
线程与进程的关系
①一个进程可以有多个线程,但至少要有一个线程,并且一个线程只能在一个进程的地址空间内活动。
②资源分配给进程,而一个进程内的所有线程共享该进程的所有资源。
③CPU分配给的是线程,即真正在CPU上运行的是线程。
④进程间通信较为复杂,同一台计算机的进程通信称为 IPC(Inter-process communication)。
而不同计算机之间的进程通信,则需要通过网络,并遵守共同的协议,例如 HTTP等。
⑤线程通信相对简单,因为它们共享进程内的内存,一个例子是多个线程可以访问同一个共享变量。
⑥线程更轻量,线程上下文切换成本一般上要比进程上下文切换低。
package Data20221010.test1.t1;
public class Wait {
public static void bySec(long s){
int i;
for ( i=1;i<=s;i++){
// System.out.println(i+"秒");
try{
Thread.sleep(1000);// 时间
}catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println(i-1+"秒");
}
}
package Data20221010.test1.t1;
public class test {
public static void main(String[] args) {
String thread = Thread.currentThread().getName();
System.out.println(thread+"线程开始运行");
Wait.bySec(10);
}
}
抢票示例:
package Data20221011.dom1;
/**
* 线程不安全的网络抢票
*
*/
public class Site implements Runnable{
private int count=10; //记录剩余票数
private int num = 0; //记录买到第几张票
public void run(){
while(true){
//没有余票时,跳出循环
if(count<=0){
break;
}
//第一步:修改数据
num++;
count--;
try {
Thread.sleep(500); //模拟网络延时
} catch (InterruptedException e) {
e.printStackTrace();
}
//第二步:显示信息
System.out.println(Thread.currentThread().getName()+"抢到第"+num+"张票,剩余"+count+"张票!");
}
}
}
package Data20221011.dom1;
public class Test {
public static void main(String[] args) {
Site site = new Site();
Thread person1= new Thread(site,"桃跑跑");
Thread person2= new Thread(site,"抢票代理");
Thread person3= new Thread(site,"黄牛党");
System.out.println("********开始抢票********");
person1.start();
person2.start();
person3.start();
}
}
// 多线程操作共享数据,容易引发数据不安全的问题