并发不一定需要依赖多线程,但是在 Java 领域,实现并发程序的主要手段就是多线程。

线程的实现

线程是比进程更轻量级的调度执行单位,线程的引入,可以把一个进程的资源分配和执行调度分开,各个线程既可以共享进程资源(内存地址、文件 I/ O 等),又可以独立调度。目前,线程是Java里进行处理器资源调度的最基本单位。

实现线程主要有三种方式:使用内核线程实现(1: 1 实现),使用用户线程实现(1: N 实现),使用用户线程加轻量级进程混合实现(N: M 实现)。Java中的线程实现在JDK1.2之前是使用的“绿色线程”,可以看作是用户线程的实现,它的好处在于,不需要系统内核的支持,缺点也在于没有系统内核的支持,如果进程中有某个线程进行了某些耗时长的操作,会阻塞整个进程。从JDK1.3开始,Java线程的实现采用的是LWP(轻量级进程),即内核线程实现(1: 1 实现)。线程的调度交给操作系统,但是这种方式的缺点在于线程消耗资源太大了,例如在linux上,一个线程默认的栈大小是1M,单机创建几万个线程就有点吃力了。所以后来在编程语言的层面上,就出现了协程这个东西。

Java 中线程的生命周期

Java语言定义了6种线程状态:

  1. NEW(初始化状态)
  2. RUNNABLE(可运行 / 运行状态)
  3. BLOCKED(阻塞状态)
  4. WAITING(无时限等待)
  5. TIMED_WAITING(有时限等待)
  6. TERMINATED(终止状态)

但其实在操作系统层面,Java 线程中的 BLOCKED、WAITING、TIMED_WAITING 是一种状态即休眠状态,也就是说只要 Java 线程处于这三种状态之一,那么这个线程就永远没有 CPU 的使用权。

Java线程的各种状态转化如下图:

注意:
上图中是有一处错误的!从RUNNABLEBLOCKED的转换只有一种可能,就是线程等待 synchronized 的隐式锁。线程调用阻塞式 API,比如以阻塞方式读文件的时候,在操作系统层面,线程是会转换到休眠状态的,但是在 JVM 层面,Java 线程的状态不会发生变化,也就是说 Java 线程的状态会依然保持 RUNNABLE 状态。
我们平时所谓的 Java 在调用阻塞式 API 时,线程会阻塞,指的是操作系统线程的状态,并不是 Java 线程的状态。

参考