程序:程序是一个静态的概念,是完成某个功能的指令集合
一、进程
进程是动态的,一般由程序、数据集合和进程控制块三部分组成,程序用于描述进程要完成的功能,是控制进程执行的指令集;数据集合是程序在执行时所需要的数据和工作区;进程控制块(PCB)包含进程的描述信息和控制信息,是进程存在的唯一标示
进程具有的特征:
动态性:进程是程序的一次执行过程,是临时的,有生命周期的,是动态产生的,动态消亡的
并发性:任何进程都可以同其他进程一起并发执行
独立性:进程 是系统进行资源分配的一个独立单位
结构性:进程由程序、数据和进程控制块三部分组成
二、线程
线程是程序执行中的一个单一的顺序控制流程,是CPU调度的最小单位,一个进程有一个或者多个线程,各个线程之间共享进程的内存空间,一个标准的线程由线程ID、当前指令指针(PC),寄存器和堆栈组成
线程的栈被自动分配到进程的内存空间中
线程和进程的区别:
(1)线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位
(2)一个进程由一个或者多个线程组成,线程是一个进程中代码的不同执行路线
(3)进程之间相互独立,但是同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(打开文件和信号),某进程内的线程在其它进程中不可见
(4)调度和切换:线程上下文切换比进程上下文切换要快的多
Java中线程堆栈是系统某个时刻的线程运行状态
三、Node.js的单线程
Node.js是单线程的,也就是说当多个用户发出请求的时候,我并不会为每个用户去创建一个线程
单线程使得Node.js不必去频繁的创建、切换线程,使得运行速度加快
单线程保证了绝对的线程安全,不必担心同一变量同时被多个线程进行读写而造成的程序崩溃
单线程的异步和非阻塞
(1)Node.js如何做到I/O的异步和非阻塞呢?
Node.js在底层访问I/O的时候还是多线程的,Node.js的fs模块的源码,里面会用到libuv来处理I/O,所以在我们看来Node.js的代码就是非阻塞和异步形式的
(2)阻塞/非阻塞与异步/同步是两个不同的概念,同步不代表阻塞,但是阻塞肯定就是同步了
举个现实生活中的例子,我去食堂打饭,我选择了A套餐,然后工作人员帮我去配餐,如果我就站在旁边,等待工作人员给我配餐,这种情况就称之为同步;若工作人员帮我配餐的同时,排在我后面的人就开始点餐,这样整个食堂的点餐服务并没有因为我在等待A套餐而停止,这种情况就称之为非阻塞。这个例子就简单说明了同步但非阻塞的情况。
再如果我在等待配餐的时候去买饮料,等听到叫号再回去拿套餐,此时我的饮料也已经买好,这样我在等待配餐的同时还执行了买饮料的任务,叫号就等于执行了回调,就是异步非阻塞了。
阻塞的单线程
既然Node.js是单线程异步非阻塞的,是不是我们就可以高枕无忧了呢?
还是拿上面那个买套餐的例子,如果我在买饮料的时候,已经叫我的号让我去拿套餐,可是我等了好久才拿到饮料,所以我可能在大厅叫我的餐号之后很久才拿到A套餐,这也就是单线程的阻塞情况。
在浏览器中,js都是以单线程的方式运行的
由如下代码:
var start = Date.now();//获取当前时间戳
setTimeout(function () {
console.log(Date.now() - start);
for (var i = 0; i < 1000000000; i++){//执行长循环
}
}, 1000);
setTimeout(function () {
console.log(Date.now() - start);
}, 2000);
最终我们的打印结果是:(结果可能因为你的机器而不同)
1000
3738
对于我们期望2秒后执行的setTimeout函数其实经过了3738毫秒之后才执行,换而言之,因为执行了一个很长的for循环,所以我们整个Node.js主线程被阻塞了,如果在我们处理100个用户请求中,其中第一个有需要这样大量的计算,那么其余99个就都会被延迟执行。
其实虽然Node.js可以处理数以千记的并发,但是一个Node.js进程在某一时刻其实只是在处理一个请求