Java并发的那些事儿(一)之基本概念

170 阅读4分钟

这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战

你必须知道的几个概念

0. 进程与线程

一般一个应用程序就是一个进程;线程是进程是子进程,可以类比为分子原子的概念,一个分子由多个原子构成,而一个进程里面也可以有很多个线程。

linux 查看进程的命令: ps -ef

linux 查看线程的命令:top -H

也可以指定进程的pid,查看该进程下的所有线程:top -H -p <pid>

1. 同步与异步

同步和异步通常来形容一次方法调用。同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。而异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。异步方法通常会在另一个线程中“真实”地执行。整个过程,不会阻碍调用者的工作。

2. 并发与并行

并发和并行都可以表示两个或多个任务一起执行,但是偏重点有些不同。并发偏重于多个任务交替进行,而多个任务之间有可能还是串行的。而并行则是真正意义地“同时执行”。

并行通常发生在多个cpu之间发生,即此时一个cpu处理一个任务,多个cpu同时处理多个任务,这多个任务即是意义上所说的并行。然而并行在真实环境中在一个cpu的系统中,使用多线程或多进程时不可能真实并行的,毕竟一个cpu一次只能执行一条指令。

3. 临界区

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

临界区资源需要加锁,以控制单个线程数量的访问。

4. 堵塞与非堵塞

堵塞与非堵塞通常用来形容多线程间的相互影响。当一个线程占用了临界区的资源,那么其它想要获得该资源的线程就必须在这个临界区中进行等待,等待导致线程挂起,此时称为这些线程是堵塞的。非堵塞强调没有一个线程可以妨碍其它线程执行。

5. 死锁、饥饿和活锁

死锁指的是多线程间资源的相互占用,同时这些线程都不愿放弃自己拥有的资源。

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

活锁通常发生在同一个资源,两个或多个线程始终保持着谦让原则,主动将资源释放给对方使用,那么资源就会不断地在这两个线程中来回跳动,而没有一个线程可以同时拿到资源而正常执行。

并发三大特性

1. 原子性

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

需要注意的是,在32位操作系统中,对于long或double类型的变量进行操作时,其操作并不是原子性操作。因为long/double类型有着64位,故在32位系统中只能先读取32位,即分一半长的数据分别操作。在多个线程对其进行写入时,其结果就会受到干扰。

2. 可见性

可见性指当一个线程对共享变量(即临界区的值)进行修改后,其它线程能立即得知这个结果。

3. 有序性

有序性指程序执行的顺序按照代码执行的先后顺序执行。由于JIT硬件优化的缘故,java在编译时会对代码进行指令重排