最近这段在看java多线程编程方面的东西。所以特写了几篇文章,来总结和回顾一下自己所学习到的相关知识。因为水平有限,文章中总结不全面甚至理解错误的地方,欢迎读者指点批评。
我们平时所接触到的程序,都是顺序编程。
顺序编程的意思是,程序中的所有事物在任意时刻都只能执行一个步骤(包括那些代码当中的顺序结构,选择结构,循环结构),
顺序编程满足了我们能够碰到的大部分问题,但是有些问题,仅仅依靠顺序编程是不够的,举个例子,一个带界面的音乐播放程序,在播放歌曲的同时也能及时的响应用户在界面上进行的的按键操作(比如点击 下一曲 按钮)。那么此时顺序编程就不合适了,因为这是需要两件同时执行的事情,如果采用顺序编程,为了及时响应按键操作,我们需要在代码的很多地方都要加上检测按键状态的代码。而这种场景下就需要并发编程了。
并发编程 有不同的实现方式。 比如 多进程模式,多线程模式等。
而最常见也最直接的就是 操作系统级别使用的进程,所谓 进程就是运行在它自己的地址空间内的自包容的程序。比如,可以在电脑上,同时并行进行几个不同的任务,qq和人聊天,酷狗播放器播放音乐,然后浏览器下着文件,这就是三个不同的进程,这三个进程彼此之间相互隔绝,不受其他进程影响。
而对于同一个进程,也可以采用多线程开发技术,使得一个程序内部多个线程可以并行运行。
当然,对于单核cpu,我们说计算机能在同一个时间点并行运行多进程或多线程。它们实际上都不是真正意义上的同一个时间点,只不过cpu切换速度极快,操作系统将cpu时间切片,分配给不同的任务。虽然看起来每个任务在执行过程中都是有时运行有时停止,但是鉴于cpu执行的速度极高,所以在我们使用者看来,那都是连续的,不间断的。这几个不同的任务就像同时并行运行一样。
而伴随着多核cpu的出现,几个核同时运行,这个时候,它们才有可能是在真正的同一个时间点并行运行。
而多线程,因为会对相同的内存空间进行并发读写操作,所以它们更加复杂。
解释一下这句话,还是 回到刚才举的那个例子,在一台电脑上,同时运行几个不同的任务。qq和人聊天,酷狗播放器播放音乐,然后浏览器下着文件。我们知道,无论什么程序,归根结底编译到了最底层,那就是0和1。因为cpu只认识0和1,如果把0和1转化为容易阅读的语言,那就是汇编语言。看过汇编的应该知道,类似 什么mov ax,bx 之类的语句都是直接操作寄存器或内存地址的。那么这个时候问题就来了,酷狗播放器,qq,浏览器这三个程序,编译到了汇编层次,那么也会直接操作寄存器和内存地址,那么为什么他们这些程序之间不冲突呢?原因就是因为 其实他们这个地址之类的,都是虚拟内存地址。酷狗播放器和qq编译成汇编语言之后,他们的内存地址都是虚拟的,所以哪怕这两个程序在汇编层面都操作同一个内存地址,也不会彼此冲突。这中间的功劳就是属于操作系统和cpu。操作系统在运行这两个进程时,会和cpu一起起作用,把他们的虚拟地址空间转换为实际的地址空间。其实说白了,就是 酷狗播放器,qq,浏览器在编写他们各自的代码的时候。根本不会考虑汇编层面上,寄存器,内存地址之类的问题。他们只需要写好自己程序本身的代码就行了。至于那些多个进程怎么运行之类的复杂问题的,上层开发的程序员们是不用关心的。
而多线程,他们归根到底还是属于同一个进程,因此多个不同线程还是对于同一内存空间进行操作,所以多线程更复杂,更容易出问题。
对于多进程,某个进程崩溃了,那么对其他进程 没啥影响,但是对于多线程,某个线程崩溃了,它所属的这个进程也会受到直接影响。
当然,我们在说不同的进程在运行的时候,都属于各自干好自己的事情,中间不会发生什么联系。这样就避免了相互干扰。不过操作系统当然也提供了进程间通信机制(IPC),比如linux的 管道,信号,套接字之类的。
而从表面上看,如果同样一个任务(进程),分解成多个线程来执行,和单独一个线程来执行,理论上前者的开销应该是更大的,原因很简单,执行多线程会增加 上下文切换的代价。但是实际上,多线程之所以更快,有两个主要因素。
(1)阻塞。程序因为程序控制之外的某些因素(比如I/0)而导致不能继续执行,cpu是如此宝贵的资源,如果cpu一直处于等待状态,那岂不是一种很大的浪费。拿i/o操作来说,cpu的执行速度极高,它的时间都是按照纳秒为单位的,但是执行程序所需要的资源或数据确往往在内存,硬盘,乃至网络服务器上,它们的速度相对于cpu那就太慢,甚至极慢。如果是顺序编程,在这种情况下,cpu只能干等着,没办法继续干活。那么这种情况下,多线程就变的很有必要了。此时cpu可以切换执行另一个任务,那么就不用干等着其中一个线程。
(2)多核cpu的出现,在最初的时候cpu都是单核的,但是到现在四核八核都很普遍的情况下,如果是顺序编程只能在某一个核上运行。找人完成一件任务,一个人单独干,和8个人协助干,虽然后者会需要花费更多的组织和协调的耗费,但是明显后者干活比前者快。
多线程编程将一个大的程序(任务/进程)划分为多个分离的,独立运行的线程(子任务),一个线程就是该进程中的 一个单一的顺序控制流,每个线程虽然没有独立的地址空间(因为只有进程才有),但是各个线程有自己的堆栈和局部变量。而在线程本身来看,就像它单独占有cpu一样。虽然底层机制是操作系统切分了cpu的时间,给它分配了这一段的时间。不过上层开发的程序员是不用关心这些的,所以这种线程模型方便了上层开发者。
-------
作者: www.yaoxiaowen.com
github: https://github.com/yaowen369