Java高级深度解析之(一)——线程和进程的由浅入深之理论分析

目录

文章前言:


 

文章前言:

常言道,所谓天下之大成者,绝不是闭门造车所成就的,凡事皆是有所学习,有所引用方能成就一番事业。此文并不是作者本人闭门造车之所得。在下也是站在巨人的肩膀上,不断学习和借鉴。学习和总结别人的知识,最终为子所用,为大家所用,本文但凡是有所引用都一定会标明引用之处。鄙人也非常感谢作者们的辛勤劳作和分享。也希望大家在看到这篇文章之后有所得,有所感。人非圣贤孰能无过。如果文章中有什么观点或者看法有异议或不足,希望大家可以给作者提出来。在这里先感谢大家了!

不知道是否与读者和笔者有同样的感觉,笔者自己感觉会很多东西,会很多技术。但是自己总是有一种感觉,对什么东西都是一知半解。而且很多东西都是感觉只知其然,而不知其所以然。越是学习就感觉到越是无知。这中感觉真的糟糕透了。特别想拜托这样的感觉。因此,我想出了一个办法: 那就是养成一个日更博客的习惯,真真正正的去学习和了解清楚 每一个知识点,在理论学习的基础上结合认真实践以解决这种困境。或许这是一种不自信吧。

以前学习热情高涨,虽然写的博客也是乱七八糟的,但是至少时不时会更新自己的博客,后来工作忙了起来(或许不是这样的,只是因为自己变得懒惰罢了)。反正我想今后的每一天我都想有所得,为自己的职业生涯留下留下轨迹。话不多说,直接开始著作吧。

这篇文章主要是讲清楚进程和线程的前世今生。

首先,会以通俗易懂的语言讲明白,说透彻进程和线程的含义,以及什么时候会用到进程和线程的知识;

然后,分析传统意义的进程和线程的创建以及使用;

再然后会讨论线程池的意义和使用。

这里的每一步不出问题的话应该都会理论分析和实践代码相结合的形式展示的。

 

进程和线程的基本概念:

进程和线程的基本概念和联系之一:

现代操作系统比如Mac OS X,UNIX,Linux,Windows等,都是支持“多任务”的操作系统。

什么叫“多任务”呢?简单地说,就是操作系统可以同时运行多个任务。打个比方,你一边在用浏览器上网,一边在听MP3,一边在用Word赶作业,这就是多任务,至少同时有3个任务正在运行。还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已。

现在,多核CPU已经非常普及了,但是,即使过去的单核CPU,也可以执行多任务。由于CPU执行代码都是顺序执行的,那么,单核CPU是怎么执行多任务的呢?

答案就是操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。

真正的并行执行多任务只能在多核CPU上实现,但是,由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。

对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程,打开一个Word就启动了一个Word进程。

有些进程还不止同时干一件事,比如Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)。

由于每个进程至少要干一件事,所以,一个进程至少有一个线程。当然,像Word这种复杂的进程可以有多个线程,多个线程可以同时执行,多线程的执行方式和多进程是一样的,也是由操作系统在多个线程之间快速切换,让每个线程都短暂地交替运行,看起来就像同时执行一样。当然,真正地同时执行多线程需要多核CPU才可能实现。

打个比方,比如我们打开Eclipse的过程中,就是相当于操作系统启动了一个进程,同时我们在Eclipse中启动了两个项目,就相当于我们启用了两个线程,这两个线程在Eclipse中启动了两个线程,这两个线程是属于Eclipse这个进程的。那么问题来了,假如我们要在这个系统中执行多个任务该怎么办呢?

有两种解决方案:

一种是启动多个进程,每个进程虽然只有一个线程,但多个进程可以一块执行多个任务。

还有一种方法是启动一个进程,在一个进程内启动多个线程,这样,多个线程也可以一块执行多个任务。

当然还有第三种方法,就是启动多个进程,每个进程再启动多个线程,这样同时执行的任务就更多了,当然这种模型更复杂,实际很少采用。

总结一下就是,多任务的实现有3种方式:

  • 多进程模式;

  • 多线程模式;

  • 多进程+多线程模式。

同时执行多个任务通常各个任务之间并不是没有关联的,而是需要相互通信和协调,有时,任务1必须暂停等待任务2完成后才能继续执行,有时,任务3和任务4又不能同时执行,所以,多进程和多线程的程序的复杂度要远远高于我们前面写的单进程单线程的程序。

因为复杂度高,调试困难,所以,不是迫不得已,我们也不想编写多任务。但是,有很多时候,没有多任务还真不行。想想在电脑上看电影,就必须由一个线程播放视频,另一个线程播放音频,否则,单线程实现的话就只能先把视频播放完再播放音频,或者先把音频播放完再播放视频,这显然是不行的。

这种解释方式是引用廖雪峰老师在Python学习中的一番阐述,能够以如此通俗易懂的方式阐述清楚进程和线程的区别与联系,笔者认为真的是一件很了不起的事情,如果有人想学习Python入门的话,可以参考链接: https://www.liaoxuefeng.com/wiki/1016959663602400/1017627212385376,当年毕业的时候公司用Python编写网站,就是看他的文章入门的。真的不错。假如,以上的描述你还是搞不懂什么是进程和线程的话,没关系,不用着急,以下的陈述我相信你肯定能够彻底的明白二者的含义。

进程和线程的基本概念和联系之二:

进程(process)和线程(thread)是操作系统的基本概念,但是它们比较抽象,不容易掌握。

最近,我读到一篇材料,发现有一个很好的类比,可以把它们解释地清晰易懂。

Java高级深度解析之(一)——线程和进程的由浅入深之理论分析_第1张图片

计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。

Java高级深度解析之(一)——线程和进程的由浅入深之理论分析_第2张图片

假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务。

Java高级深度解析之(一)——线程和进程的由浅入深之理论分析_第3张图片

进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。

Java高级深度解析之(一)——线程和进程的由浅入深之理论分析_第4张图片

一个车间里,可以有很多工人。他们协同完成一个任务。

Java高级深度解析之(一)——线程和进程的由浅入深之理论分析_第5张图片

线程就好比车间里的工人。一个进程可以包括多个线程。

Java高级深度解析之(一)——线程和进程的由浅入深之理论分析_第6张图片

车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存

Java高级深度解析之(一)——线程和进程的由浅入深之理论分析_第7张图片

可是,每间房间的大小不同,有些房间最多只能容纳一个人,比如厕所。里面有人的时候,其他人就不能进去了。这代表一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。

Java高级深度解析之(一)——线程和进程的由浅入深之理论分析_第8张图片

一个防止他人进入的简单方法,就是门口加一把锁。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫"互斥锁"(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。

Java高级深度解析之(一)——线程和进程的由浅入深之理论分析_第9张图片

还有些房间,可以同时容纳n个人,比如厨房。也就是说,如果人数大于n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。

Java高级深度解析之(一)——线程和进程的由浅入深之理论分析_第10张图片

这时的解决方法,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了。这种做法叫做"信号量"(Semaphore),用来保证多个线程不会互相冲突。

不难看出,mutex是semaphore的一种特殊情况(n=1时)。也就是说,完全可以用后者替代前者。但是,因为mutex较为简单,且效率高,所以在必须保证资源独占的情况下,还是采用这种设计。

Java高级深度解析之(一)——线程和进程的由浅入深之理论分析_第11张图片

操作系统的设计,因此可以归结为三点:

(1)以多进程形式,允许多个任务同时运行;

(2)以多线程形式,允许单个任务分成不同的部分运行;

(3)提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。

以上的观点和分析来源于大咖阮一峰老师的分享: https://cloud.tencent.com/developer/article/1467031,我相信脑袋瓜子再不怎么好使的人,其实已经能够了解个十之八九了。


记得,在上大学的时候,学习过一门课程《操作系统》,里面用了很官方和书面的措辞讲解了关于进程,线程,程序的概念,以及之间的区别和联系。

在操作系统中进程和线程的描述大概是这样的。

1、程序只是一组指令的有序集合,它本身没有任何运行的含义,它只是 一个静态的实体。而进程则不同,它是程序在某个数据集上的执行。进程是一个动态的实体,它有自己的生命周期。它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而被撤消。反映了一个程序在一定的数据集上运行的全部动态过程。

2、进程和程序并不是一一对应的,一个程序执行在不同的数据集上就成为不同的进程,可以用进程控制块来唯一地标识每个进程。而这一点正是程序无法做到的,由于程序没有和数据产生直接的联系,既使是执行不同的数据的程序,他们的指令的集合依然是一样的,所以无法唯一地标识出这些运行于不同数据集上的程序。一般来说,一个进程肯定有一个与之对应的程序而且只有一个而一个程序有可能没有与之对应的进程(因为它没有执行),也有可能有多个进程与之对应(运行在几个不同的数据集上)

3、进程还具有并发性和交往性,这也与程序的封闭性不同。


进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。进程和线程的区别在于:

简而言之,一个程序至少有一个进程,一个进程至少有一个线程. 
线程的划分尺度小于进程,使得多线程程序的并发性高
另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率
线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.

综上所述: 关于线程和进程的理论只是基本上就是属于这样的,首先,进程是系统进行资源分配和调度的独立单位;然而,线程是进程的一个实体,是CPU调度和分派的基本单位,(这里有两个概念: 基本单位和独立单元,记得当年在《操作系统》考试的时候有这么一个判单题目。当年应该是做错了的,存在模棱两可的情况。而且在今天的大公司面试中也会存在这样的面试题目,希望读者能够真正的理解二者的含义。),可以这么去理解: 在一个应用程序启动的时候呢,也就是开启了一个进程,在这个进程开启的同时呢很多相关的东西都被确认了下来(比如,内存区域的划分,进程ID是多少,这个进程有一个初始线程是干什么的,比如打开Word的时候,首先开启的是拼写检查的线程等)。很多东西真的需要静下心来认真理解。

关于进程线程理论知识的解读就先到这里了。假如以后遇到更好的案例,我们会继续分析和分享。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(Java高级深度解析)