Java Concurrent 背景&基础概念&操作系统

在写Java Concurrent之前的铺垫。

前言:

并发可能在许多刚接触编程的程序员眼中显得高大上或者多余,因为刚接触编程时不是很理解 并发的背景、意义,并且并发编程通常相对于串行执行的程序要复杂一些。

1、性能是最主要的原因。大约零几年的时候,因特尔正式宣布4GHZ芯片研发计划取消,代表着单核性能提升逐渐缓慢,但同时CPU多核形式呈现到人们眼前,在这种大环境下,为了充分发挥CPU性能,充分利用CPU资源 并发编程开始火热起来。

2、针对复杂的业务模型,并发编程更适合完成业务需求,比如一个下单流程,存在绑券、入库、生成订单、消息分发等多个操作。我们可以以多线程的形成区分业务模块,并且当这些操作并行执行时,响应时间可以倍数减少。这里多线程并发编程更像是一种解耦策略,它帮助我们把做什么(目的)&什么时候做(时机) 分开,应用程序的吞吐量存在显著的提升。

如果觉着好像没接触过多线程并发编程,其实仅仅可能是不知道而已。Java web中的servlet就是最经典的单例多线程模型,可以尝试着在servlet开一个变量,然后尝试并发访问,如果觉着程序执行太快,不好模拟,尝试一下sleep。

因为性能,因为业务,因为资源,我们也算是被迫选择了并发编程。

刚开始大家可能会对并发编程存在什么误解,比如说

1、并发编程一定能改善性能

2、并发编程使用既有工具即可(比如concurrenthashmap、lock什么的)

在接触并发编程之前,首先需要了解一些基础概念,省的以后的学习过程中出现什么奇妙的看法。

基础概念

1、先搞清楚业务场景是那种类型

IO密集型:IO(input、output)(磁盘IO、网络IO等)。IO密集型指的是系统的CPU性能相对硬盘、内存要好很多,此时,系统运作,大部分的状况是CPU在等I/O (硬盘/内存) 的读/写操作,此时CPU Loading并不高,I/O bound的程序一般在达到性能极限时,CPU占用率仍然较低。这可能是因为任务本身需要大量I/O操作,而pipeline做得不是很好,没有充分利用处理器能力。

CPU密集型:也称为计算密集型,指的是系统的硬盘、内存性能相对CPU要好很多,此时,系统运作大部分的状况是CPU Loading 接近100%,I/O在很短的时间就可以完成,而CPU还有许多运算要处理,CPU Loading很高。

2、线程 or 进程

1)线程共享内存空间,进程的内存是独立的。

2)同一个进程的线程之间可以直接交流,两个进程想通信,必须通过一个中间代理来实现。

3)创建新进程很简单,创建新进程需要对其父进程进行一个克隆。

4)一个线程可以控制和操作同一进程里的其他线程,但是进程只能操作子进程。

5)改变注线程(如优先权),可能会影响其他线程,改变父进程,不影响子进程。

6)线程是操作系统能够进行运算调度的最小单位(程序执行流的最小单元),进程是线程是操作系统能够进行运算调度的最小单位(程序执行流的最小单元)。

都有各自的优势,并不是说那个好哪个坏,需要根据具体场景做具体的适配,Java是典型的多线程并发,PHP多进程并发(最近刚看的)。

Java 线程与操作系统线程

操作系统线程模型:(几种古老的模型,但是对于理解现在复杂的操作系统模型提供了很大帮助。ps:以下三点为引用片段,非原创)

1)、线程实现在用户空间下

当线程在用户空间下实现时,操作系统对线程的存在一无所知,操作系统只能看到进程,而不能看到线程。所有的线程都是在用户空间实现。在操作系统看来,每一个进程只有一个线程。过去的操作系统大部分是这种实现方式,这种方式的好处之一就是即使操作系统不支持线程,也可以通过库函数来支持线程。

这种模式最致命的缺点也是由于操作系统不知道线程的存在,因此当一个进程中的某一个线程进行系统调用时,比如缺页中断而导致线程阻塞,此时操作系统会阻塞整个进程,即使这个进程中其它线程还在工作。还有一个问题是假如进程中一个线程长时间不释放CPU,因为用户空间并没有时钟中断机制,会导致此进程中的其它线程得不到CPU而持续等待。

2)、内核线程就是直接由操作系统内核(Kernel)支持的线程,这种线程由内核来完成线程切换,内核通过操纵调度器(Scheduler)对线程进行调度,并负责将线程的任务映射到各个处理器上。每个内核线程可以视为内核的一个分身,这样操作系统就有能力同时处理多件事情,支持多线程的内核就叫做多线程内核(Multi-Threads Kernel)。

3)、在这种混合实现下,即存在用户线程,也存在轻量级进程。用户线程还是完全建立在用户空间中,因此用户线程的创建、切换、析构等操作依然廉价,并且可以支持大规模的用户线程并发。而操作系统提供支持的轻量级进程则作为用户线程和内核线程之间的桥梁,这样可以使用内核提供的线程调度功能及处理器映射,并且用户线程的系统调用要通过轻量级进程来完成,大大降低了整个进程被完全阻塞的风险。在这种混合模式中,用户线程与轻量级进程的数量比是不定的,即为N:M的关系。

Java 线程:

1)首先要明确一点,Java 代码是在JVM上运行的,然后JVM与操作系统直接交互。就当前阶段而言,Java 线程与操作线程存在什么样的关系需要看JVM具体实现的映射关系,不同的平台通常是不一致的。

2)就当前阶段,Java 实现、Linux 平台来说,都是由一对一映射到操作系统线程的。

写到这里已经大致描述了并发编程的历史原因及一些并发编程中的一些基础概念和操作系统常识。