进程和线程的区别
百度往往得到的答案是:“进程是资源分配的最小单位,线程是CPU调度的最小单位”,十个有九个看不明白,太官方。
- 进程:可以理解为我们window上的一个程序(一个程序可能包含一个或多个进程)。
- 线程:进程中的一个执行任务,一个进程可以包含多个线程 。
进程切换和线程切换
最初计算机都是依靠进程切换来完成CPU的调度的,而不同的进程,对应不同的虚拟地址空间,切换时,涉及到该空间的切换。而线程属于进程,当多个属于同一进程的线程之间的切换时,是不涉及到虚拟地址切换的问题的,开销自然少了很多,故现在的操作系统都是依赖线程切换来完成CPU的调度的。
并发编程的三个概念
首先要明白一个概念,CPU、内存、IO三者之间的速度存在着巨大的差异(CPU > 内存 > IO),但往往性能的瓶颈取决的木桶最短的那个木板,硬件工程师们为了平衡三者之间的差异,做出了如下的优化:
- CPU增加了缓存(三级缓存,有兴趣的可以自行百度),以拉近与内存之间的差异;
- 增加了进程、线程的概念,复用CPU,进而增加CPU的使用效率,来拉近与IO之间的差异;
在理解了上面两个概念后,我们其实可以类比日常开发使用的技术,比如IO多路复用(NIO)、平常说的,用redis增加缓存,减少数据库的操作(redis对应内存,数据库是IO操作,内存更快)等。接下来,我们介绍下有关并发编程的三个重要概念:
可见性
概念:一个线程对共享变量的修改,另一个线程能够立即看到,我们成为可见性。
对着上面的原型图,当两个线程运行在不同的CPU上时,他们操作的CPU缓存分属于不同的CPU,相互是独立的,故要保证数据可见性,需要借助中间的内存来做同步操作。在单核时代,只有一个CPU,对应的缓存也只有一个,故不存在同步的问题(类比线程1和线程2);
原子性
概念:一个或多个操作,在CPU执行过程中,要么全部成功,要么全部失败。
线程切换其实会带来原子性问题,这里回顾下进程和线程的知识,一个进程包含多个线程,一个线程包含多个执行命令,而CPU在线程切换时,是发生在每个执行命令执行完的时候。我们平常使用的高级语言,发送的一条命令,往往对应计算机多条执行命令,最常见的,num++操作,刚接触java的时候也以为它是原子性的,其实不然,它对应3条命令:从内存中读取num的值到CUP缓存、执行+1操作,将结果写回内存中。
有序性
概念:顾名思义,就是程序按照代码指定的先后顺序执行;
public static void main(String[] args) {
int a = 1;
int b = 2;
System.out.println(a + b);
}
如上面的代码所示,编译器可能会先执行b = 2的操作,再执行a = 1的操作。编译器在保证最终结果与代码执行结果一致的前提下进行优化,这种情况叫指令重排序。