java高并发入门篇(基本概念)

走入并行的世界

在学习java程序高并发设计的初期,下面这些文章以及概念,是你不得不掌握的。

你必须知道的几个概念

现在,虽说很多人(包括Linus在内)都觉得并行计算或者说并行算法是多么奇葩。但不得不承认,在某些领域,服务端编程还是需要大量并行计算,而java也主要占领着服务端市场,java的并行计算研究当然是大势所趋。

1. 同步和异步

同步和异步通常形容一次方法调用,同步方法调用一旦开始,调用者必须等到方法执行返回后才能继续后续的操作。异步方法调用更像一个消息的传递,一旦开始,方法调用就会立即返回,调用者便可以继续后续操作。在这里插入图片描述
举个列子或许你就能明白,比如你去拉面馆吃面,你点了一份拉面,你需要在拉面馆里一直等待拉面师傅把拉面做好,放好调料,由服务生端到你的面前,你吃完拉面,结完账单,一次购买拉面体验结束,这就是一次同步的调用。还用一种方法,你在网上直接下单,支付完费用,当你完成这笔订单后,对你来说,此处购买拉面过程已经结束,虽然拉面还没有到家,但是你的任务已经完成。商家收到订单后,会加紧安排制作与送货。这些一切都与你无关,此时你想干嘛就干嘛,这就是异步调用。

2. 并行和并发

并行和并发是两个非常容易被混淆的概念。它们都可以表示两个或者多个任务一起执行,但是侧重点不同。并发偏重于多个任务交替执行,而多个任务之间有可能还是串行的,而并行是真正意义上的“同时执行”。
在这里插入图片描述.实际上,如果系统只有一个CPU,而使用多进程或者多线程任务,实际环境中不可能真实并行,毕竟一个CPU一次只能执行一条指令,在这种情况下多进程或者多线程就是并发的,而不是并行的(操作系统会不断切换多个任务而已),真实的并行只可能出现在多CPU中。

3. 临界区

临界区用来表示一种公共资源或者说共享资源,可以被多个线程使用。但是每一次,只能有一个线程使用它,一旦临界区被占用,其它线程必须等待。


比如,用打印机举例,打印机可以打印不同的文件,但是每次只能打印一个,其余文件需要等待当前文件打印完成后才能按特定的顺序进行逐个打印。在并行程序中,如果A,B两个文件同时使用打印机,同时打印,那么打印出的文件就可能是两个文件的综合,则既不是A文件,也不是B文件,这就导致了文件的错乱。

4. 阻塞和非组赛

阻塞和非阻塞通常用来形容多线程之间的影响。
阻赛一个线程占用临界区资源,其余线程需要使用该资源就必须等待占用线程使用完毕才能继续使用。如果占用线程一直不释放,那么其它线程就全部阻塞在此处无法进行。
非阻赛与阻塞相反,它强调没有任何线程会影响其它线程的进行。

5. 死锁(Deadlock),饥饿(Starvation),活锁(Livelock)

死锁,饥饿和活锁都属于多线程活跃性的问题。如果出现上诉问题,多线程可能不再活跃甚至无法执行下去
死锁是最糟糕的情况,如图所示
在这里插入图片描述
A,B,C,D四个小车在此时都无法前行,互相占用它车道路,切都不愿释放当前占用的车道,导致这种状态一直进行,产生死锁,死锁是一个很严重的并且是最应该避免和时时小心的问题。
产生死锁的四个必要条件
1 互斥条件:
进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。

2 不可剥夺条件:
进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。

3 请求与保持条件:
进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

4 循环等待条件:
存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求。即存在一个处于等待状态的进程集合{Pl, P2, …, pn},其中Pi等 待的资源被P(i+1)占有(i=0, 1, …, n-1),Pn等待的资源被P0占有,如图2-15所示。

直观上看,循环等待条件似乎和死锁的定义一样,其实不然。按死锁定义构成等待环所 要求的条件更严,它要求Pi等待的资源必须由P(i+1)来满足,而循环等待条件则无此限制。 例如,系统中有两台输出设备,P0占有一台,PK占有另一台,且K不属于集合{0, 1, …, n}。

Pn等待一台输出设备,它可以从P0获得,也可能从PK获得。因此,虽然Pn、P0和其他 一些进程形成了循环等待圈,但PK不在圈内,若PK释放了输出设备,则可打破循环等待, 如图2-16所示。因此循环等待只是死锁的必要条件。

资源分配图含圈而系统又不一定有死锁的原因是同类资源数大于1。但若系统中每类资 源都只有一个资源,则资源分配图含圈就变成了系统出现死锁的充分必要条件。

以上这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

预防死锁
破坏“互斥”条件:
就是在系统里取消互斥。若资源不被一个进程独占使用,那么死锁是肯定不会发生的。但一般来说在所列的四个条件中,“互斥”条件是无法破坏的。因此,在死锁预防里主要是破坏其他几个必要条件,而不去涉及破坏“互斥”条件。

注意:互斥条件不能被破坏,否则会造成结果的不可再现性

破坏“占有并等待”条件:
破坏“占有并等待”条件,就是在系统中不允许进程在已获得某种资源的情况下,申请其他资源。即要想出一个办法,阻止进程在持有资源的同时申请其他资源。
方法一:创建进程时,要求它申请所需的全部资源,系统或满足其所有要求,或什么也不给它。这是所谓的 “ 一次性分配”方案。
方法二:要求每个进程提出新的资源申请前,释放它所占有的资源。这样,一个进程在需要资源S时,须先把它先前占有的资源R释放掉,然后才能提出对S的申请,即使它可能很快又要用到资源R。

破坏“不可抢占”条件:
破坏“不可抢占”条件就是允许对资源实行抢夺。
方法一:如果占有某些资源的一个进程进行进一步资源请求被拒绝,则该进程必须释放它最初占有的资源,如果有必要,可再次请求这些资源和另外的资源。
方法二:如果一个进程请求当前被另一个进程占有的一个资源,则操作系统可以抢占另一个进程,要求它释放资源。只有在任意两个进程的优先级都不相同的条件下,方法二才能预防死锁。

破坏“循环等待”条件:
破坏“循环等待”条件的一种方法,是将系统中的所有资源统一编号,进程可在任何时刻提出资源申请,但所有申请必须按照资源的编号顺序(升序)提出。这样做就能保证系统不出现死锁。


饥饿 指某一个或多个线程由于某种原因一直无法执行。比如它的优先级较低,高优先级线程一直抢占其资源,导致自身一直无法执行。与死锁相比,它可能会在一些时间后(高优先级线程全部执行完毕后)得到执行,它是有可能自身得到解决的。它也是非公平锁带来的弊端。


活锁 是一种非常有趣的情况,这是一种互相谦让的行为,线程自身认为我应该释放自己现在持有的资源给其它线程使用,而其它线程也是同样的想法,这就是线程”智力“不够的情况,这样会导致资源不断在多个线程中跳动,而没有一个线程可以同时获取所有资源,导致无法正常执行,形成活锁。

发布了3 篇原创文章 · 获赞 1 · 访问量 131

猜你喜欢

转载自blog.csdn.net/qq_41961781/article/details/103831662