1.同步(Synchronous)和异步(Asynchronous)
同步和异步通常用来形容一次方法的调用,同步方法一旦开始,调用者就必须要等到方法调用返回后,才能继续后续的行为;异步方法更像是一个消息的传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作,而异步方法通常会在另外一个线程中"真实"的执行,整个过程,不会阻碍调用者的工作。
2.并发(Concurrency)和并行(Parallelism)
并发和并行都可以表示两个或者多个任务一起执行,所以这两个概念非常容易混淆,但是并发和并行的偏重点不同。并发偏重于多个任务交替执行,而多个任务之间有可能还是串行执行的;而并发则是真正意义上的“同时执行”。
严格意义上来说,并行的多个任务是真实的同时执行,对于并发来说,这个过程只是交替的,一会儿执行任务A一会儿执行任务B,系统会不停的在任务之间切换;但是对于外部观察者来说,即使多个任务之间是串行并发的,也会造成多个任务间是并行执行的错觉。
实际上,如果系统只有一个CPU,而使用多进程或者多线程任务,那么在真实的环境中这些任务不可能是真实并行的,毕竟一个CPU一次只能执行一条指令,这种情况下多进程或者多线程就是并发的不是并行的。真实的并行也只可能出现在拥有多个CPU的系统中。
但是:并发的最终效果可能和并行是一样的,所以两者的区别在没有特别需要的情况下,没有必要区分。
3.临界区
临界区用来表示一种公共资源或者共享数据,可以被多个线程使用。但是在大多数的情况下,只能有一个线程使用它,一旦临界区资源被占用,其他线程要想使用这个资源,就必须等待;比如:在一些读写分离分布式的情况下,当你只读的时候,就不必限制线程的数量。
在并发程序中,临界区资源就是保护的对象,防止出现脏读,幻读等。
4.阻塞(Blocking)和非阻塞(Non-Blocking)
阻塞和非阻塞通常形容并发模式下的线程之间的相互影响。比如一个线程占用了临界区资源,那么其他需要这个资源的其他线程就必须等待,导致线程挂起,这就是阻塞。非阻塞的意思与其相反,它强调没有一个线程可以妨碍其他线程执行,就是非阻塞线程不会挂起,而是会去做其他的事情。
5.死锁(Deadlock)、饥饿(Starvation)和活锁(LiveLock)
死锁(Deadlock)是最糟糕的一种情况,其定义为:两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。
死锁产生的四个特定条件:
❤ 互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到该进程释放。
❤ 请求和保持条件:是指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占用,此时请求进程阻塞,但又对自己已获得的其他资源保持不放。
❤ 不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
❤ 环路等待条件:指发生死锁时,必然存在一个进程--资源的环形链,即进程集合{P0,P1,P2...,Pn}中的P0在等待一个P1占用的资源,P1正在等待P2占用的一个资源,....,Pn正在等待P0占用的资源。
饥饿(Starvation)是指一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执行。比如:有可能它的线程优先级太低,而高优先级线程不断抢占它需要的资源,导致优先级低的线程无法执行。
活锁(Livelock)指两个线程都秉承“谦让”的原则,主动将资源释放给他人使用,那么就会出现资源在两个线程中跳动,而没有一个线程可以同时拿到所有资源而正常执行,这种情况就是活锁。