Java高并发基础篇:并发与线程(一)

0x01、你必须知道线程是什么?

线程是轻量级进程,是程序执行的最小单位

在介绍线程之前,我们必须要知道进程的概念。***进程(Process)*是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。进程是程序的基本执行体,线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
##进程和线程是什么关系?

进程是线程的容器,一个进程可以容纳若干个线程。

我们可以把进程比喻成一个工厂的车间,线程就是车间里每个流水线上的工人,车间里的设备就是进程内的资源,工人在各自的流水线上使用设备生产,就像线程各自占用进程里的资源执行任务一样。

0x02线程的生命周期或状态

Java高并发基础篇:并发与线程(一)

线程的所有状态在Thread中的State枚举中定义:

public enum State {
        NEW,
        RUNNABLE,
        BLOCKED,
        WAITING,
        TIMED_WAITING,
        TERMINATED;
    }
  • NEW:刚刚创建的线程,这种线程还未开始执行。
  • RUNNABLE:线程的start()方法调用时,表示线程开始执行,线程所需要的一切资源都准备好了。
  • BLOCKED:当线程在执行过程中遇到synchronized同步块,就会进入BLOCKED阻塞状态,线程暂停运行,直到获取资源的锁。
  • WAITING: 进入一个无时间限制的等待。
  • TIMED_WAITING:进入一个有时限的等待状态。
  • TERMINATED:线程执行完毕,表示结束。

注意:从NEW状态触发后,线程不能斜体样式再回到NEW状态,同理,处于TERMINATED的线程也不能再回到RUNNABLE状态。

0x03并发的几个概念

同步(Synchronous)和异步(Asynchronous)

  • 同步:方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续行为。
  • 异步:方法调用更像一个消息的传递,一旦开始,方法调用就会立即返回,调用者可以继续后续的操作。
    Java高并发基础篇:并发与线程(一)

临界区

临界区用来表示一种公共资源或者说是共享数据,可以被多个线程使用。但一次只能由一个线程来使用它,其他线程想要使用,必须等待。
在并行程序中,临界区资源是保护的对象。

阻塞(Blocking)和非阻塞(Non-Blocking)

阻塞和非阻塞通常用来形容多线程间的相互影响。当一个线程占用了临界区资源,其他需要这个资源的线程必须在临界区等待。等待会导致线程挂起,这种情况就是阻塞。
非阻塞,强调没有一个线程可以妨碍其他线程的执行。

死锁(Deadlock)、饥饿(Starvation)和活锁(Livelock)

死锁、饥饿和活锁都属于多线程的活跃性问题,如果出现上述几种情况,那么相关线程可能就不再活跃,也就很难再继续执行下去。

  • 死锁:两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁

产生死锁的四个必要条件:

(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系。
Java高并发基础篇:并发与线程(一)

  • 饥饿:一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执行。如,线程优先级太低或者某一个线程一直占着关键资源不放,需要这个资源的线程无法正常执行。与死锁相比,饥饿是有可能在未来一段时间内解决的(比如高优先级的线程已经完成任务,不再执行)。
  • 活锁:当两个线程秉承“谦让”原则,主动将资源释放给对方使用,那么就会出现资源不断在两个线程中跳动,而没有一个线程可以拿到所有资源而正常执行。这种情况就是活锁。
    #0x04并发级别
    由于临界区的存在,多线程之间的并发必须受到控制。根据并发的策略,可以把并发的级别进行分类,可分为阻塞、无饥饿、无障碍、无锁、无等待几种。
    ##阻塞(Blocking)
    一个线程是阻塞的,那么在其他线程释放资源之前(即当前线程未得到临界区的锁),当前线程无法继续执行(挂起等待)。使用synchronized关键字和重入锁得到的就是阻塞的线程。

无饥饿(Starvation-Free)

如果线程之间是有优先级的,那么线程调度的时候总是会倾向于满足高优先级的线程。也就是说,对于同一资源的分配是不公平的。这种非公平的锁来说,系统允许优先级高的线程插队。就可能会导致低优先级的线程产生饥饿。
##无障碍(Obstruction-Free)
无障碍是一种最弱的非阻塞调度。无障碍执行不会因为临界区的问题导致一方被挂起,任何线程都可以进入临界区,修改共享数据。对于无障碍的线程来说,一旦检测到多方同时修改共享数据的情况,它会立即对自己所做的修改进行回滚,确保数据安全。

无锁(Lock-Free)

无锁的并行都是无障碍的,所有线程都能尝试对临界区进行访问。
##无等待(Wait-Free)
无等待是一种在无锁的基础上更进一步进行扩展,它要求所有线程都必须在有限步内完成,这样就不会引起饥饿问题。

0x05并发的原子性、可见性和有序性

  • 原子性(Atomicity):指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。
  • 可见性(Visibility):指当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道这个修改。
  • 有序性(Ordering):即程序执行的顺序按照代码的先后顺序执行。但是一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。指令重排可以保证串行语义一致,但是没有义务保证多线程间的语义也一致。