【2019-06-11】并发编程基本知识整理

1.同步和异步

同步和异步通常用来形容一次方法的调用,同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为;异步方法调用,会在另外一个线程中执行,调用者可以继续后续的操作,如果异步调用需要返回结果,那么在完成后通知调用者。

2.并行和并发

并行和并发是两个容易混淆的概念,并行是真正意义上的同时执行,并发则是多个任务交替执行,而他们之间则还是串行执行的。

3.临界区

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

4.阻塞和非阻塞

阻塞和非阻塞通常用来形容多线程间的相互影响,比如一个线程占用了临界区资源,那么其他线程就必须等待,这种情况就是阻塞;非阻塞强调没有一个线程可以妨碍其他线程执行,其他线程都会不断向前执行。

5.死锁(deadlock)、饥饿(starvation)、活锁(livelock)

死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法继续执行。

死锁的条件

<1>互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放

<2>请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。 

<3>不抢占条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他抢占 

<4>循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。

避免死锁:

<1>加锁顺序:多线程加锁的顺序不同,很容易发生死锁,如果加锁顺序一致,则发生死锁的可能就会降低很多

<2>加锁时限:增加获取锁的超时机制,如果一个线程长时间无法获取锁,则进行回退,释放其已获得的锁资源,等待一段时间后再进行重试

<3>死锁检测:每次记录线程获取锁和请求锁的记录,当下一个线程请求锁失败的时候,就去查询锁的关系图,看是否有死锁发生;若有,则可以进行回退,再等待一段时间后进行重试;更好的方式是设置线程优先级,让低优先级的线程释放锁,使得其他线程可以执行下去。

饥饿是指一个或多个线程因为某些原因无法获得所需的资源,导致一直无法执行。比如线程优先级太低,而高优先级的线程不断抢占它需要的资源。

活锁是指多个线程抢占资源的时候都秉着“谦让”的原则,主动将资源释放给其他线程使用,那么久会出现没有一个线程可以同时拿到所有资源而正常执行。


6.并发级别

由于临界区的存在,多线程之间的并发必须受到控制,根据控制并发的策略,可以对并发的级别进行分类,大致上可以分为阻塞,无饥饿,无障碍,无锁,无等待。

<1>阻塞

当使用synchronized关键字,或者重入锁的时候,线程尝试获取锁,如果获取不到,则会被挂起等待。

<2>无饥饿

对于非公平的锁,系统允许高优先级的线程插队,这样有可能导致低优先级线程产生饥饿;对于公平锁,满足先来后到,那么饥饿就不会产生。

<3>无障碍

无障碍是一种最弱的非阻塞调度,如果两个线程是无障碍的执行,那么他们不会因为临界区的问题导致一方被挂起。但是如果同时修改数据,会通过检测,如果检测到则立即对修改进行回滚,确保安全。

<4>无锁

无锁的并行都是无障碍的,在无锁的情况下,所有的线程都能尝试对临界区进行访问,但是不同的是,无锁的并发保证必然有一个线程能够在有限步内完成操作离开临界区。

<5>无等待

无等待则在无锁的基础上进行扩展,它要求所有线程都必须在有限步内完成,这样就不会引起饥饿问题。

一个典型的无等待结构就是RCU(Read-Copy-Update),基本思想是对数据的读可以不加控制,所以所有的线程都是无等待的,但是在写数据的时候,先取得原始数据的副本,接着只修改副本数据,修改完成后,在合适的时机回写数据。


7.Java的原子性、可见性和有序性

<1>原子性

原子性是指一个操作是不可中断的,即使是多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰

<2>可见性

可见性是指当一个线程修改了一个共享变量的值,其他线程是否能够立即知道这个修改。

<3>有序性

有序性问题的原因是因为程序在执行时,可能会进行指令重排,重排后的指令与原指令的顺序未必一致。

指令重排可以保证串行语义一致,但是没有义务保证多线程间的语义也一致

8.happen-before原则

程序顺序原则:一个线程内保证语义的串行性

volatile规则:volatile变量的写,先发生于读,保证了volatile变量的可见性

锁规则:解锁必然发生在随后的加锁前

传递性:A先于B,B先于C,那么A必然先于C

线程start方法先于它的每一个动作

线程的所有操作先于线程的终结

线程的终端先于被中断的线程的代码

对象的构造函数执行、结束先于finaized方法

你可能感兴趣的:(【2019-06-11】并发编程基本知识整理)