调用线程的start(),线程真的会马上执行吗?|七日打卡

1,315 阅读7分钟

前言

问几个问题

为什么我们调用线程的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 处理完毕,线程重新转入就绪状态。

死亡状态: 一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

敲重点

单核CPU执行多线程原理:宏观并行,微观串行。\color{blue}{单核CPU执行多线程原理:宏观并行,微观串行。}

多核才是真正并行执行的,并行线程数看有多少个处理核心。如i7有4核8线程。

看图中线程和进程数,就算是多核的CPU你看我们计算机中往往运行上百个进程和上千个线程。也不可能同时运行上千个线程,所以操作系统采用时间分配机制,每个进程能获取到20-200ms的时间片运行,(时间片不同的系统可能不太一样,那都大同小异。)时间片用完了就切换给下一个进程了,记录进程信息在进程管理块PCB中,然后进入就绪状态队列,等待下一次分配时间片。举一个例子吧!上老演员。

这里用单核操作系统说明:

把进程比作人

比如你6个人用一个洗手间,为啥是一个洗手间呢,因为多进程在微观上面是交替执行的。所以6个人轮流使用一个洗手间,这就叫多线程发行\color{blue}{多线程发行}, 由于系统多进程是时间切片,即每个人只能用一会,比如这里是30秒,就得换下一个了。这个时候拉到一半怎么办?这个时候就需要你夹断了 (这就是系统中断机制\color{blue}{系统中断机制},进程管理块PCB,会保存这个人拉到那里了,等待下次再上洗手间)。然后接着下一个,以此类推。

其实这样很浪费资源的, 如果其中三个人其实是不急的或者根本没有需求,进去也是,浪费资源。所以这个时候就出现优化,没有需求,先在外面等着。(这就是阻塞状态\color{blue}{阻塞状态}了), 把这个三个人放到阻塞队列,这时洗手间压力减轻一半,可以愉快上个洗手间了。等他们有需求再从新排队,在接着.....就是阻塞状态变成就绪状态,排队,等待系统分配时间片-执行状态。)

看看图

本来想手画一个的,画了几遍,算了,灵魂画家,我都忘记我是一个业余摄影师的,直接拍图处理一下不就行了嘛。

加入多线程概念,进程管理块PCB也被扩展到包括每个线程的信息,还有一些其他改变以便支持多线程。现代系统中一个进程可以有多个线程,多核计算机(多个洗手间~_~)是可以同时执行多个线程的,这样大大增加效率。(cup直接打满呀!) 切换进程是开销大的,而切换线程开销小,好比,你把三个人放到厕所里面,即马桶边上,这个过程少了开关门的步骤,是不是更加有效率了,手动微笑。

谢谢

到此相信你们都知道答案了。

参考

《Java并发编程之美》

《操作系统概念》第九版

《操作系统》第四版

本人知识有限,如有描述错误之处,愿虎正。

你看这个像不像你欠我的赞。 谢谢大家。你的赞就像冬日暖阳,温暖心窝。