Java并发编程之线程篇之线程的由来(一)

前言

在Java并发编程中线程的使用尤其重要。了解线程的由来,使用场景及注意事项是做为一个合格的Java程序员必备的技能。本文章中会对线程的由来、进程与线程的区别、及线程的使用场景进行简单介绍。但愿经过该文章,小伙伴们能对线程有一个更深的了解。html

从操做系统发展了解线程

线程的出现,离不开进程。而进程的出现又离不开操做系统。操做系统的发展促进了线程与进程的技术崛起。因此了解操做系统的发展,对咱们理解线程尤其重要。整个操做系统大体分为以下几个阶段:程序员

  • 手工操做
  • 单道批处理系统
  • 多道批处理系统
  • 分时操做系统
  • 等等等

在前三个阶段中,对进程与线程的理解尤其重要,故文章会着重介绍前三个阶段。算法

手工操做

最先的计算机并位出现真正意义上的操做系统,这时的计算机智能解决简单的数学问题,好比正铉,余铉等。其运行方式也特别简单,程序员将对应于程序和数据的已穿孔的纸带(或卡片)装入输入机,而后启动输入机把程序和数据输入计算机内存,接着经过控制台开关启动程序针对数据运。计算完毕后,输出机输出计算结果;用户取走结果并卸下纸带(或卡片)后,才让下一个用户上机。举个简单的列子,假设咱们须要向计算机发送吃饭、洗澡、睡觉这三个指令,那么在传统计算机中,咱们能够获得下图:编程

手动操做.png

从上图中咱们能够明显看出,手工操做方式严重损害了系统的利用率。在等待用户输入指令时,计算机一直处于闲置状态。segmentfault

单道批处理系统

为了摆脱手动操做带来的耗时性,实现做业(程序、数据、指令)的自动过渡。接着又出现了单道批处理系统。单道批处理系统在原来手动操做主要的区别是在输入机与主机之间增长了一个存储设备磁带(盘)(下图,红色虚线部分),并在主机系统上配上监督程序,其具体运行方式一般是把一批做业以输入到磁带上,而后由监督程序将磁带上的第一个做业装入内存,并把运行控制权交给该做业。当该做业处理完成时,又把控制权交还给监督程序,再由监督程序把磁带(盘)上的第二个做业调入内存。计算机系统就这样自动地一个做业一个做业地进行处理,直至磁带上的全部做业所有完成。仍是以上文吃饭、洗澡、睡觉这三个指令为例子,咱们能够获得下图:markdown

单道批处理系统.png

须要注意的是,虽然单道批处理操做系统可以解决手动操做时须要人工切换做业致使的系统利用率低的问题,可是又由于单道批处理系统是将做业一个一个加入内存的,那么某一个做业由于等待磁带(盘)或者其余I/O操做而暂停时,那计算机就只能一直阻塞,直到该I/O完成。对于CPU操做密集型的程序,I/O操做相对较少,所以浪费的时间也不多。可是对于I/O操做较多的场景来讲,CPU的资源是属于严重浪费的。多线程

多道批处理系统

为了解决单道批处理系统由于输入/输出(I/O)请求后,致使计算机等待I/O完成而形成的计算机的资源的浪费。接下来又出现了多道批处理系统。多道批处理系统与单道批处理系统的主要区别是在内存中容许一个或多个做业,当一个做业在等待I/O处理时,多批处理系统会经过相应调度算法调度另一个做业让计算机执行。从而使CPU的利用率获得更大的提升。以下图所示:并发

多道批处理系统.png

进程的由来

在多道批处理系统中引伸出了一个很是重要的模式,即容许多个做业进入内存并运行。因为在内存中存储了多个做业,那么多个做业如何进行区分?当某个做业由于等待I/O暂停时,怎么恢复到以前的运行状态呢?异步

因此这个时候,聪明的人们就发明了进程这一律念,用进程来保存每一个做业的数据与运行状态,同时对每一个进程划分对应的内存地址空间(代码、数据、进程空间、打开的文件),而且要求进程只能使用它本身的内存空间。那么就能够达到做业的区分及恢复。分布式

线程的由来

多道批处理系统所引入的进程的概念,确实提升了计算机的运行效率,可是单单使用进程来处理程序的并发的弊端也越来与突出,由于一个进程在一个时间段内只能作一件事情。若是某个程序有多个任务,只能逐个执行这些任务。

并发:宏观上看起来同一个时间段有多个任务在计算机中执行。

仍是以上文提到的吃饭为例子。假设在吃饭进程中包含两个任务,一个看电视任务,一个吃饭任务。如今咱们但愿计算机一边执行吃饭任务,一边执行看电视任务。那么根据多道批处理系统的运行规则,计算机实际调度是以进程为调度单位的,那么咱们想一边吃饭,一边看电视的行为,只能拆分为两个进程。可是咱们知道进程中存储了大量信息(数据,进程运行状态信息等)。那么当计算机进行进程切换的时候,必然存在着很大的时间与空间消耗(由于每一个进程对应不一样内存地址空间,进程的切换,实际是处理器处理不一样的地址空间)。若是不拆分为两个进程,吃饭这两个任务在同一个进程中,那么这两个任务必然是依次执行的,这又违背了咱们一边看电视一边吃饭的初衷。

那么问题来了,咱们是否能够实现进程中任务的切换,又能够避免进程切换内存地址空间呢?聪明的人们又进一步的发明了线程这一律念。用线程表示进程中的不一样任务,同时又将计算机实际调度的单元转到线程。这样就避免了进程的内存地址空间的切换,也达到了多任务的并发执行。具体以下图所示:

调度区别.png

进程与线程的区别

前面讲解了进程与线程的由来,有可能你们如今还有一丝疑惑,下面就让咱们一块儿来总结一下进程与线程的关系。

  • 进程是计算机分配资源的单元,而线程是计算机调度的基本单元。
  • 一个进程由一个或多个线程组成。线程表明着进程中不一样的任务。
  • 进程之间相互独立,同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号)
  • 进程的切换由时空开销。

进程与线程的关系以下图所示:

进程线程的关系.png

线程的使用场景

线程的出现确实提升了程序运行的性能,那么线程的使用场景有哪些呢?

  • 执行后台任务,在不少场景中,可能会有一些定时的批量任务,好比定时发送短信、定时生成批量文件。在这些场景中能够经过多线程的来执行。
  • 异步处理,好比在用户注册成功之后给用户发送优惠券或者短信,能够经过异步的方式来执行,一方面提高主程序的执行性能;另外一方面能够解耦核心功能,防止非核心功能对核心功能形成影响。
  • 分布式处理,好比fork/join,将一个任务拆分红多个子任务分别执行。
  • BIO模型中的线程任务分发,也是一种比较常见的使用场景,一个请求对应一个线程。

线程使用注意事项

虽然在程序中咱们能够建立多个线程来提升程序的运行效率与吞吐率,可是也会出现如下问题:

  • 因为多个线程共同占有所属进程的资源和地址空间,那么多个线程要同时访问某个资源,这个时候该怎么处理?
  • 线程既然能提升运行效率,那么是否在程序中建立越多线程越好?

这些问题实际上是关于线程同步与合理的建立线程数的问题。这里就不作过多的介绍,若是你们对这部分感兴趣,能够关注后续文章或查阅相关资料。

最后

站在巨人的肩膀上,才能看的更远~

相关文章
相关标签/搜索