天天看点

进程与线程【入门篇】

本博文只对进程和线程做理论讲解,先带领各位小伙伴走进进程和线程的大门。

进程

-- 进程是一个具有一定独立功能的应用程序关于某个数据集合上的一次运行活动。      -----------------  一个Application对应一个进程,应用程序是静态的,进程是动态的。

-- 进程是线程的容器,每个进程至少包含一个线程;      ------------------ 进程与线程是一对多的关系。

-- 进程是操作系统进行资源分配和调度的最小单位,资源包括CPU、内存空间、磁盘IO等;

-- 每个进程都是相互独立的,不能进行资源共享;

-- 进程分为系统进程和用户进程,系统进程就是各个系统应用所启动的进程,而系统应用是为了满足操作系统本身各项功能的应用;用户进程就是我们终端用户启动的进程,比如QQ、微信、Office办公软件等。

线程

-- 线程是CPU调度的最小单位,必须依赖于进程而存在;

-- 线程本身基本不拥有系统资源,只拥有运行中必不可少的程序计数器、一组寄存器和栈,但线程之间可以共享同属进程中的全部系统资源。

-- 线程无处不在,任何程序都必须创建线程,线程才是真正做事的(就像我们程序猿&媛一样),在Java程序中,必须由main函数作为程序入门启动主线程,在Android中进行网络请求等耗时操作,我们必须开子线程,不能阻塞UI线程。

CPU核心数和线程数的关系

多核心

单芯片多处理器简称CMP(Chip Multiprocessors),也称多核心处理器。是指在一个芯片上集成多个微处理器核心,各个处理器并行执行不同的进程。这种依靠多个CPU同时并行地运行程序是实现超高速计算的一个重要方向,称为并行处理。

多线程

Simultaneous Multithreading,简称SMT。使得同一处理器上的多个线程同步执行并共享处理器的执行资源。

先前CPU是单核的,只有一个线程,目前主流CPU都是多核的,增加核心数就是为了增加线程数,我们都知道操作系统并不能执行任务,真正执行任务的是线程,核心数越多,表明可以并行的线程数越多,执行任务效率就越高。一般情况下,核心数与线程数是1:1的关系,比如我们常见的四核CPU一般拥有4个线程,但Intel引入超线程技术后,使得核心数与线程数形成了1:2的关系,如图1所示,4个内核对应8个逻辑处理器。

进程与线程【入门篇】

图1

多核心与多CPU傻傻分不清楚

我刚开始也以为多核心和多CPU不是一回事,CPU是中央处理器,就跟我们中国只有一个党中央,每个国家只有一个领袖人物一样,但我忘记了最牛的领导都是无为而治,懂得分权和用人,凡事都亲力亲为的领导会累死,所以CPU也是如此,从外观来看,我们的电脑或者笔记本确实只有一个CPU,但是CPU内部又是由多个微处理组成,每一个微处理器就是一个核心,也就是一个微型CPU,如此来看,多核心可以理解为多CPU。

CPU时间片轮转机制

细心的小伙伴可能发现了,在图1中逻辑处理器只有8个,却有3865个线程,也就是说线程数远超核心数,并不是1:2的关系,这不是和先前讲的冲突了吗?但是请你再细心一点,先前讲的1:2的关系是建立在并行的前提下的,8个核心就是8条道路,可以满足8个线程同时并行于这8条道路上。但我们又有疑问了,我们平时开发的时候开的软件可不止8个,并没有感觉到任何卡顿的情况,而且我们撸码的时候,想启动线程就启动线程,不用顾忌那么多事情,为什么?这就得益于操作系统为我们提供的CPU时间片轮转机制,也叫RR调度(Round-robin scheduling)。在RR调度算法中,将一个较小时间单元定义为时间量或时间片。时间片的大小通常为 10~100ms。就绪队列作为循环队列。CPU调度程序循环整个就绪队列,为每个进程分配不超过一个时间片的CPU。

​​点击这里查看​​百度百科关于时间片轮转机制的解释

维基百科上关于RR调度有这样一段解释:

To schedule processes fairly, a round-robin scheduler generally employs time-sharing, giving each job a time slot or quantum[4] (its allowance of CPU time), and interrupting the job if it is not completed by then. The job is resumed next time a time slot is assigned to that process. If the process terminates or changes its state to waiting during its attributed time quantum, the scheduler selects the first process in the ready queue to execute. In the absence of time-sharing, or if the quanta were large relative to the sizes of the jobs, a process that produced large jobs would be favoured over other processes.

Round-robin algorithm is a pre-emptive algorithm as the scheduler forces the process out of the CPU once the time quota expires.

For example, if the time slot is 100 milliseconds, and job1 takes a total time of 250 ms to complete, the round-robin scheduler will suspend the job after 100 ms and give other jobs their time on the CPU. Once the other jobs have had their equal share (100 ms each), job1 will get another allocation of CPU time and the cycle will repeat. This process continues until the job finishes and needs no more time on the CPU.

ps:RR调度唯一有趣的一点是时间片的长度。从一个进程切换到另一个进程是需要定时间的,包括保存和装入寄存器值及内存映像,更新各种表格和队列等。假设进程切换(也叫上下文切换)需要5ms,时间片设置为20ms,则在做完20ms有用的工作之后,CPU还得花费5ms进行进程切换,这样25%的时间被浪费在了管理开销上。为了提高CPU 效率,我们可以将时间片设为5000ms。这时浪费的时间只有0.1%。但考虑到在一个分时系统中,如果有10 个交互用户几乎同时按下回车键,将发生什么情况?假设所有其他进程都用足它们的时间片的话,最后一个不幸的进程不得不等待5秒才获得运行机会。多数用户无法忍受一条简短命令要5秒才能做出响应,同样的问题在一台支持多道程序的个人计算机上也会发生。结论可以归结如下:时间片设得太短会导致进程频繁切换,降低了CPU效率,而设得太长又可能引起对短的交互请求的响应变差。将时间片设为100ms通常是一个比较合理的折衷。

并发

指应用能够交替执行不同的任务,比如单核CPU执行多线程并非是同时执行多个任务,而是以我们不可察觉的速度不断去切换这些任务,以达到"同时执行效果"。当谈论并发的时候一定要加个单位时间,也就是说单位时间内并发量是多少?离开了单位时间其实是没有意义的。

并行

指应用能够同时执行不同的任务,比如吃饭的时候可以边吃饭边训熊孩子,这两件事情可以同时执行。(吃饭的时候尽量别训小孩,会影响胃消化食物)

高并发编程的好处和注意事项

高并发编程的好处

<1>充分利用CPU资源

目前市面的计算机都是多核CPU,如果你用的i3的CPU,最差的也是双核四线程,而你只用一个线程处理任务,那么就浪费了3/4的CPU性能,这就好比你开三万的月薪招了一个高级开发,却给他分配三千月薪的开发实习生就能做的工作,明显大材小用,浪费了公司资源。

<2>加快响应速度

我们在用百度云或者迅雷下载东西的时候,一般都是好多个任务一块下载,而不是一个一个去下载,为什么呢?快呀,古语有云:三个臭皮匠赛过诸葛亮嘛~

<3>方便业务拆分

例如我们实现电商系统,下订单和给用户发送短信、邮件就可以进行拆分,将给用户发送短信、邮件这两个步骤独立为单独的模块,并交给其他线程去执行。这样既增加了异步的操作,提升了系统性能,又使程序模块化,清晰化和简单化。

注意事项

<1>线程之间的安全性

在同一个进程里面的多线程是资源共享的,也就是都可以访问同一个内存地址当中的一个变量。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说这个全局变量是线程安全的;但如果多个线程同时执行写操作,一般都需要考虑线程同步问题,否则就可能引入线程安全问题。

<2>线程之间的死锁

为了解决线程之间的安全性引入了Java的锁机制,而一不小心就会产生Java线程死锁的多线程问题,因为不同的线程都在等待那些根本不可能被释放的锁,从而导致所有的工作都无法完成。假设有两个线程,分别代表两个饥饿的人,他们必须共享刀叉轮流吃饭。他们都需要获得两个锁:共享刀和共享叉的锁,假如线程A 获得了刀,而线程B 获得了叉。线程A 就会进入阻塞状态来等待获得叉,而线程B 则阻塞来等待线程A 所拥有的刀。这只是人为设计的例子,但尽管在运行时很难探测到,这类情况却时常发生。

<3>服务器资源被耗尽导致死机

继续阅读