前言
问几个问题
为什么我们调用线程的start(),不是调用run()?
调用线程的start(),线程会马上执行?
操作系统分配给线程运行时间是多少?
好吧!让我们开始....
进程的定义
聊线程之前,让我们简单认识一下进程。
进程的定义“进程”这一术语在20世纪60年代初期首先于美国麻省理工学院的MULTICS系统和IBM公司的CTSS/360系统中引入。 进程是操作系统中的一个最基本也是最重要的概念。掌握这个概念对于理解操作系统的实质,对于分析、设计操作系统都具有非常重要的意义。 但是,迄今为止,进程的概念仍未有一个非常确切的、统一的定义。有许多人从不同角度对“进程”下过各种定义, 下面是几个操作系统的权威人士对进程所下的定义。
(1)行为的一个规则称为程序,程序在处理机上执行时所发生的活动称为进程(Dijkstra)。
(2)进程是可以和别的计算并发执行的计算(Madnick and Donowan)。
(3)进程是一个程序及其数据在处理机上顺序执行时所发生的活动(A.C.Shaw)。
(4)进程是程序在一个数据集合上的运行过程,是系统进行资源分配和调度的一个独立单位(Peter Denning)。
什么是线程?
因为线程是进程中的一个实体,线程本身是不会独立存在的。进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位, 线程则是进程的一个执行路径,一个进程中至少有一个线程,进程中的多个线程共享进程的资源。
在早期的操作系统中并没有线程的概念,进程是能拥有资源和独立运行的最小单位,也是程序执行的最小单位。
引入线程的好处
(1)创建一个新线程花费时间少(结束亦如此)。创建线程不需另行分配资源,因而创建线程的速度比创建进程的速度快,且系统的开销也少。
(2)两个线程的切换花费时间少。
(3)由于同一进程内的线程共享内存和文件,线程之间相互通信无须调用内核,故不需要额外的通信机制,使通信更简便,信息传送速度也快。
(4)线程能独立执行,能充分利用和发挥处理机与外围设备并行工作的能力。
为什么调用线程的start(),线程不能马上执行
new Thread(){
//等待调度
@Override
public void run() {
super.run();
System.out.println("running....");
}
}.start();
}
start方法后线程并没有马上执行而是处于就绪状态,这个就绪状态是指该线程已经获取了除CPU资源外的其他资源, 等待获取CPU资源后才会真正处于运行状态。一旦run方法执行完毕,该线程就处于终止状态。
先解释一下线程的状态,再解释cup调用和分配的时间片大小。
线程的五大状态分别为:
新建状态: 使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
就绪状态: 当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
运行状态: 如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
阻塞状态: 如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
死亡状态: 一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
敲重点
多核才是真正并行执行的,并行线程数看有多少个处理核心。如i7有4核8线程。
看图中线程和进程数,就算是多核的CPU你看我们计算机中往往运行上百个进程和上千个线程。也不可能同时运行上千个线程,所以操作系统采用时间分配机制,每个进程能获取到20-200ms的时间片运行,(时间片不同的系统可能不太一样,那都大同小异。)时间片用完了就切换给下一个进程了,记录进程信息在进程管理块PCB中,然后进入就绪状态队列,等待下一次分配时间片。举一个例子吧!上老演员。
这里用单核操作系统说明:
把进程比作人
比如你6个人用一个洗手间,为啥是一个洗手间呢,因为多进程在微观上面是交替执行的。所以6个人轮流使用一个洗手间,这就叫, 由于系统多进程是时间切片,即每个人只能用一会,比如这里是30秒,就得换下一个了。这个时候拉到一半怎么办?这个时候就需要你夹断了 (这就是,进程管理块PCB,会保存这个人拉到那里了,等待下次再上洗手间)。然后接着下一个,以此类推。
其实这样很浪费资源的, 如果其中三个人其实是不急的或者根本没有需求,进去也是,浪费资源。所以这个时候就出现优化,没有需求,先在外面等着。(这就是了), 把这个三个人放到阻塞队列,这时洗手间压力减轻一半,可以愉快上个洗手间了。等他们有需求再从新排队,在接着.....就是阻塞状态变成就绪状态,排队,等待系统分配时间片-执行状态。)
看看图
本来想手画一个的,画了几遍,算了,灵魂画家,我都忘记我是一个业余摄影师的,直接拍图处理一下不就行了嘛。
加入多线程概念,进程管理块PCB也被扩展到包括每个线程的信息,还有一些其他改变以便支持多线程。现代系统中一个进程可以有多个线程,多核计算机(多个洗手间~_~)是可以同时执行多个线程的,这样大大增加效率。(cup直接打满呀!) 切换进程是开销大的,而切换线程开销小,好比,你把三个人放到厕所里面,即马桶边上,这个过程少了开关门的步骤,是不是更加有效率了,手动微笑。
谢谢
到此相信你们都知道答案了。
参考
《Java并发编程之美》
《操作系统概念》第九版
《操作系统》第四版
本人知识有限,如有描述错误之处,愿虎正。
你看这个像不像你欠我的赞。 谢谢大家。你的赞就像冬日暖阳,温暖心窝。