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

走入并行的世界

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

你必须知道的几个概念

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

1. 同步和异步

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

2. 并行和并发

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

3. 临界区

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


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

4. 阻塞和非组赛

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

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

死锁,饥饿和活锁都属于多线程活跃性的问题。如果出现上诉问题,多线程可能不再活跃甚至无法执行下去
死锁是最糟糕的情况,如图所示
java高并发入门篇(基本概念)
A,B,C,D四个小车在此时都无法前行,互相占用它车道路,切都不愿释放当前占用的车道,导致这种状态一直进行,产生死锁,死锁是一个很严重的并且是最应该避免和时时小心的问题。关于死锁的详细介绍,大家可以百度或者在我后续章节里将有详细介绍。


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


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