Java并发编程系列文章
《一》多线程基础——Java线程与进程的基本概念
《二》多线程基础——Java线程入门类和接口
《三》多线程基础——Java线程组和线程优先级
《四》多线程基础——Java线程生命周期及转换
《五》多线程基础——Java线程间的通信(互斥与协作)
《六》实际应用——如何优雅的关闭线程
《七》实际应用——生产者与消费者模型
并发编程(多线程)
一直以来都是程序员头疼的难题。曾经听别人总结过并发编程的第一原则,那就是不要写并发程序,哈哈哈。后来发现,这样能够显著提高程序响应和吞吐量的利器,哪还能忍得住不会用呢?
整理出《Java并发编程系列文章》,共计7篇,与君共勉。
《一》多线程基础——Java线程与进程的基本概念
1、概念和区别
1.1、进程
一个正在计算机中运行的应用程序就是有一个进程
,各个进程在计算机内存中分配了独立空间,互不干扰。计算机CPU
采用时间片轮转的方式运行进程。对于单核CPU
来说,任意具体时刻都只有⼀个任务在占用CPU资源。
如果在时间片结束时进程还在运⾏,则暂停这个进程的运行,并运行另⼀个分配了时间片的进程(这个过程叫做上下⽂切换
)。
如果进程在时间片结束前阻塞或结束,则CPU
立即进行切换,不⽤等待时间片用完。当进程暂停时,它会保存当前进程的状态(进程标识,进程使用的资源等),在下⼀次切换回来时根据之前保存的状态进行恢复,接着继续执行。
1.2、线程
如果⼀个进程有多个子任务时,只能逐个得执行这些子任务,很影响效率。所以让一个进程包含了多个线程,每个线程负责⼀个单独的子任务,事情就变得简单多了,只需要让计算机CPU
将时间片分配给线程,就能让一个进程实现多个任务同时(依然是时间片交替分配执行)进行。
例如:当用户使用扫描病毒功能时,就让扫描病毒这个线程去执行。同时,如果用户⼜使用清理垃圾功能,那么可以先暂停扫描病毒线程,先响应用户的清理垃圾的操作,让清理垃圾这个线程去执行。响应完后再切换回来,接着执行扫描病毒线程。
总之,进程和线程的提出极大的提高了操作提供的性能。进程让操作系统的并发性成为了可能,而线程让进程的内部并发成为了可能。
1.3、进程和线程的区别
进程是⼀个独立的运⾏环境,⽽线程是在进程中执⾏的⼀个任务。他们两个本质的区别是是否单独占有内存地址空间及其它系统资源(⽐如I/O):
- 进程单独占有⼀定的内存地址空间,所以进程间存在内存隔离,数据是分开 的,数据共享复杂但是同步简单,各个进程之间互不干扰;而线程共享所属进 程占有的内存地址空间和资源,数据共享简单,但是同步复杂。
- 进程单独占有⼀定的内存地址空间,⼀个进程出现问题不会影响其他进程,不 影响主程序的稳定性,可靠性⾼;⼀个线程崩溃可能影响整个程序的稳定性, 可靠性较低。
- 进程单独占有⼀定的内存地址空间,进程的创建和销毁不仅需要保存寄存器和 栈信息,还需要资源的分配回收以及页调度,开销较大;线程只需要保存寄存 器和栈信息,开销较小。
另外⼀个重要区别是,进程是操作系统进⾏资源分配的基本单位,⽽线程是操作系统进⾏调度的基本单位,即CPU
分配时间的单位 。
2、上下文切换
上下文切换(有时也称做进程切换或任务切换)是指 CPU
从⼀个进程(或线程)切换到另⼀个进程(或线程)。上下文是指某⼀时间点 CPU
寄存器和程序计数器的内容。
寄存器是cpu内部的少量的速度很快的闪存,通常存储和访问计算过程的中 间值提⾼计算机程序的运⾏速度。
程序计数器是⼀个专⽤的寄存器,⽤于表明指令序列中 CPU 正在执⾏的位 置,存的值为正在执⾏的指令的位置或者下⼀个将要被执⾏的指令的位置,
具体实现依赖于特定的系统。 举例说明 线程A - B
1.先挂起线程A,将其在cpu中的状态保存在内存中。
2.在内存中检索下⼀个线程B的上下⽂并将其在 CPU 的寄存器中恢复,执⾏B 线程。
3.当B执⾏完,根据程序计数器中指向的位置恢复线程A。
CPU
通过为每个线程分配CPU
时间⽚来实现多线程机制。CPU
通过时间⽚分配算法来循环执⾏任务,当前任务执⾏⼀个时间⽚后会切换到下⼀个任务。
但是,在切换前会保存上⼀个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。所以任务从保存到再加载的过程就是⼀次上下⽂切换。
上下⽂切换通常是计算密集型的,意味着此操作会消耗⼤量的 CPU 时间,故线程也不是越多越好。