Java线程之线程概述

不知从什么时候开始,似乎到处都充斥着高并发的味道,或许是源于网络购票曾经遇到过的种种困难,或许是由于各个购物网站的各种促销,于是乎在各种招聘启事中都明确要求有丰富的高并发、可扩展性等要求,那么多少数量的并发才可以称之为高并发呢?该如何评价自己是否具有高并发服务器程序的技能和经验呢?对这些问题,本人不能给出明确的答案,首先是因为没有开发过高并发的服务器程序(参与过的大型项目是基于ssh的web应用),其次在接触计算机编程的年代,到处都在鼓吹Java面向对象的各种优点,诸如不用操心内存的分配和释放,可移植性(其实很多语言都具有可移植性),不用重复造轮子(工作后才发现能够自己制造轮子是多么的幸福),Java多线程非常容易实现,Java提供了各种数据结构集合等,于是很多底层基础知识都直接跳过了,等到发现如果想要更进一步弄清楚某些问题的根源时,往往就会发现到处都是瓶颈,甚至都不知道自己需要百度什么。于是工作后开始重新阅读操作系统、算法等相关书籍,当再次学习这些基础却非常重要的知识时,总会得到不同的启示,也许是因为上学时纯粹是为了应付考试。

出自兴趣和工作的需求,接触到大数据相关领域,也开始使用开源的大数据存储、处理和分析工具,比如Hadoop、Hive和HBase,还接触到了OpenStack云计算框架,其中工作用到的是Swift模块。这些软件也好,框架也罢,都在标榜自己具有高并发性、高可用性和扩展性,于是就在思考它们是如何实现高并发的呢?由于自己好歹有点Java编程知识,自然而然的就想到了多线程,创建了Java多线程后就一定是高并发了吗?答案显然是不一致的。有的情况使用多线程确实可以得到更高的效率,比如一个件任务可以被分为互不影响的子任务,无论是在单处理器还是多处理器上都可以得到效率的提升,相反如果一个任务是顺序执行的,后面的代码必须等到前面的代码执行完毕才能继续执行,那么多线程反而有可能降低效率。由此可以简单的得出结论多线程的使用要依赖于具体的场景,这也是很多开源软件在介绍某个参数时非常喜欢使用的一句话,但对于我来说却非常不喜欢这句话,因为这并没有给我明确唯一的答案(中国学生总喜欢明确唯一的答案)。为了寻找最爱,只好再一次拿起《操作系统概念》,并准备好了Hadoop和HBase的源代码,方便随时参考和查阅具体实现。

1.1线程概念

尽管多线程编程的概念已经非常普及,还是有必要重申一下线程的概念及其与进程的关系。线程是运行于CPU中的基本单元,对于即使不支持多线程的系统来说——尽管这样的系统少之又少,但确实存在过——这一说法也是成立的。或许有人要问运行于CPU中的基本单元不是进程吗,那么就有必要澄清一下进程的概念,尽管到处都在谈论进程。广义上或者通俗地讲,进程指的是运行中的程序,因此是活动的实体,更具体地来说,进程包括程序代码(文本段或代码段),程序计数器的值和寄存器集合中的内容,数据段,堆栈等。而线程是进程代码的执行流,包括线程ID、程序计数器、寄存器集合和线程栈,与属于同一进程的其它线程(如果存在的话)共享代码段、数据段和其它操作系统资源,比如打开文件和信号等。由于线程具有进程的某些属性,如程序计数器、寄存器等,因此也被称为轻量级进程,而传统意义上的进程则被称为重量级进程,可以用下面的图形象地描述单线程进程和多线程进程的差别:

从图中可以清晰地发现,在单线程进程中线程就是进程,在多线程进程中存在多个线程,也就意味着可以同时做许多任务(同时在单处理器中并不是真正意义上的在同一时刻,而是由于处理器超快的处理速度给使用者一种近乎真实同时的错觉,但在多处理器中却可以做到真正的同一时刻)。还可以确定的是线程必属于某个进程且不能独立存在于进程之外,而进程则至少拥有一个线程。

既然线程仅是进程中的执行序列,而多线程进程中可以同时存在多个执行序列以完成多个任务,那为什么不将多个线程拆分到多个进程中来完成多个任务,或者说在某个系统中仅存在重量级进程而不存在轻量级进程,反过来说,为什么不将所有的任务都合并到一进程中,即整个系统只存在一个进程,该进程由众多的线程组成。答案显然是非常复杂的,简单地讲,之所以不将多个线程创建为多个进程,是因为:

1.属于同一进程的所有线程共享共同的数据,它们之间的通信不需要进程间通信的额外开销。

2.由于共享相同的资源,创建线程通常比创建进程更加快速(只需要创建所使用的线程栈),且线程的上下文切换比进程的上下文切换同样更加高效。

3.对某些服务,比如打印机或者web服务器,多线程往往比多进程能够提供更加高效的响应速度,因为同一进程中的线程间通信往往比进程间的通信更加高效。

既然多线程存在这么多优点,为什么不将所有的进程合并为一个进程,该进程中的线程将完成之前所有进程的工作。显然这样的设想是不成立的,因为操作系统本身就不仅仅是一个单独的进程,而是由若干完成不同任务的进程组成,或者说由不同的模块组成。那退一步讲,假设操作系统仅仅是由一个进程组成,所有用户的任务由一个进程组成,这势必会招致安全问题,因为所有用户的任务共享相同的内存空间,彼此可以访问对方的数据,而如果想避免这些问题则会增加操作系统在安全方面的复杂性。另外从软件工程的角度讲,将不相干的任务合并为一个进程违反了松耦合的原则,比如将打印机和web服务器或者浏览器放在一个进程中,这种强行粘合的行为带来的危害性远远大于多线程提供的诸多优点。

1.2上下文切换

在上文中几次提到上下文切换这一概念,笼统地讲,上下文切换指的是将CPU的使用权由一个进程或者线程交由另一个进程或线程,而涉及的细节往往与特定操作系统或硬件有密切的关系。进程的上下文切换指的是不同进程之间的轮流获得CPU的使用权,比如当一个进程等待IO的完成,则要交出CPU的使用权,另一个等待CPU的进程获得使用权进而可以执行。线程的上下文切换指的是同一进程中的线程轮流获得CPU的使用权,不同进程的线程上下文切换即为进程的上下文切换。在单处理器的系统中,无论是进程内的线程上下文切换还是进程间的上下文切换都容易理解,因为仅有单个CPU,进程内的线程上下文切换仅需要保存之前运行的线程的状态,然后载入即将运行线程的状态,而进程间的线程上下文切换实际为进程上下文切换,不管这些进程是单线程还是多线程的。在多处理器的系统中,进程内的线程上下文切换与单处理器的情况相同,但进程间的线程上下文切换是进程上下文切换还是等同于进程内的线程上下文切换呢?如果要执行上下文切换的线程属于单线程进程,那么显然是进程上下文切换,如果是多线程进程呢?在此需要明确的一点时,进程内的线程上下文切换不需要改变进程的地址空间,而进程上下文切换则需要改变进程的地址空间,由于不同进程的线程拥有不同的地址空间,进程间的线程上下文也需要改变地址空间,因此即使是多处理器系统,进程间的线程上下文切换也是进程上下文切换。根据以上的论述可以得出,在不明确指出进程间线程上下文切换的情况下,线程上下文切换即为进程内的线程上下文切换,因此只有这一种情况线程上下文切换比进程上下文切换高效,而不论单CPU还是多CPU系统,进程间的上下文切换都等价于进程上下文切换。

1.3线程类型人生伟业的建立,不在能知,乃在能行。

Java线程之线程概述

相关文章:

你感兴趣的文章:

标签云: