JVM相关及其拓展(六) -- Java与线程

Java与线程

线程的实现

线程是比进程更轻量级的调度执行单位,线程的引入可以把一个进程的资源分配和执行调度分开,各个线程间既可以共享进程资源,又可以独立调度(线程是CPU调度的基本单位)。

实现线程主要有三种方式:

  • 使用内核线程实现

    直接由操作系统内核支持的线程。

    由内核来完成切换,内核通过操纵调度器对线程进行调度,并负责将线程的任务映射到各个处理器上。

    多线程内核:有能力处理多件事情,支持多线程的内核

    轻量级进程:内核线程的一种高级接口。只有先支持内核线程,才能有轻量级进程

    • 优点:每个轻量级进程都是一个独立的调度单元,即使有一个在系统调用中堵塞了,也不影响整个进程继续工作。
    • 缺点:各种线程操作都需要进行系统调用,代价相对高,需要在用户态和内核态中来回切换。另外轻量级进程的数量是有限的。
    • 轻量级进程与内核线程是1:1的关系
  • 使用用户线程实现

    广义:一个线程只要不是内核线程,就可以认为是用户线程

    狭义:完全建立在用户空间的线程库上,而系统内核不能感知线程存在的实现

    • 优点:线程的建立、同步、销毁和调度都在用户态中完成,不需要内核参与,所以操作时非常快速且低消耗,还支持更大的线程数量
    • 缺点:没有系统内核的支持,所有线程操作都需要用户程序自己处理,实现较复杂
    • 进程与用户线程之间是1:N的关系
  • 使用用户线程加轻量级进程混合实现

    既存在用户线程,也存在轻量级进程。

    • 优点:用户线程还是在用户空间中,还可以支持大规模的用户线程并发;轻量级进程可以作为内核线程和用户线程之间的桥梁,用户线程的系统调用需要轻量级进程来完成,大大降低了系统被阻塞的危险。
    • 采用多对多的线程模型。

Java线程的实现是不能确定的。由于操作系统支持怎样的线程模型,在很大程度上决定了Java虚拟机的线程是怎样映射的。

线程调度

系统为线程分配处理器使用权的过程。

主要调度方式有两种:

  • 协同式线程调度

    线程的执行时间有 线程本身 控制,线程把自己的工作执行完后,要主动通知系统切换到另一个线程上。

    • 优点:实现简单,切换操作可知,基本不存在线程同步问题
    • 缺点:线程执行时间不可控
  • 抢占式线程调度

    每个线程由系统分配执行时间,线程的切换不由线程本身决定

    • 线程执行时间是可控的,不存在因为一个线程而堵塞整个系统的问题

    • 可以设置线程优先级,优先级越高的线程越容易被系统选择执行

      线程优先级并不是太靠谱,一方面线程调度还是取决于操作系统,优先级的实现不会太一致。另一方面优先级会被系统自行改变。

线程状态转换

在任意时间点,一个线程有且只有一个状态

线程状态转换
  • 新建

    线程创建后尚未启动的线程状态

  • 运行

    包括正在执行和等待着CPU为它分配执行时间

  • 无限期等待

    不会被分配CPU执行时间,要等待被其他线程显示的唤醒。以下方法会让线程陷入无限期的等待状态:

    1. 没有设置Timeout参数的Object,wait()
    2. 没有设置Timeout参数的Thread.join()
    3. LockSupport.park()
  • 限期等待

    不会被分配CPU执行时间,但在一定时间后会被系统唤醒。以下方法会让线程进入限期等待状态:

    1. Thread.sleep()
    2. 设置Timeout参数的Object,wait()
    3. 设置Timeout参数的Thread.join()
    4. LockSupport.parkNanos()
    5. LockSupport.parkUntil()
  • 阻塞

    线程被阻塞了 在程序等待进入同步区域的时候进入这种状态。

    阻塞状态:等待着获取到一个排他锁,将在另一个线程放弃这个锁的时候发生

    等待状态:在等待一段时间或者唤醒动作的发生

  • 结束

    线程已经结束执行


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!