Java线程模型、线程状态 - 线程(1)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010297957/article/details/51162058

1. 概述

众所周知,线程 - Thread 是比进程 - Progress 更轻量级的调度单位。简单来说,引入线程带来的好处是:

可以把一个进程资源分配执行调度分开,各个线程 既可以共享进程 资源(内存地址、文件I/O等),又可以独立调度。

线程实现方式:
主流的操作系统都实现了线程 ,而编程语言一般会提供关于线程 的统一API操作。那么,编程语言如何去调用系统线程 呢?这方面主要有3种方式:

  1. 使用内核线程 - Kernel Thread
    一对一线程模型 ,这个最重要,下面详细讲。

  2. 使用用户线程 - User Thread。
    一对N线程模型 ,用户程序完全模拟线程的行为,编码时操作的线程是用户程序模拟的线程,操作系统内核不能感知你做了创建、调度等等线程操作。许多编程语言最初使用过这种方式,但现在基本放弃了这种。

  3. 使用用户线程 + 轻量级进程混合实现。
    N对M线程模型 ,也有很多语言使用混合实现。

2. Java线程模型

  1. JVM线程模型的选择:
    首先,JVM规范没有限定JVM要使用哪种线程模型,具体的JVM采用什么线程模型与很多因素包括所在的OS有关;
    其次,线程模型只对线程的并发规模和操作成本产生影响。
    所以,线程模型对于我们程序员编码来说是透明的。

  2. SUN JDK中:
    对于官方JDK来说,因为Windwos和Linux操作系统(主流)只提供一对一线程模型 ,所以SUN JDK采用了一对一线程模型 。其它JDK可以有不同实现(如Solaris JDK可以采用N对M模型)。

  3. 内核线程方式:
    如上所述的三中方式中,SUN JDK采用的内核线程 。这种方式的线程调度流程如下:

    这里写图片描述

    调度流程:

    1. 首先,从OS方面来说:如果操作系统内核中包括了一个线程调度器(Scheduler) ,内核可以操作调度器对内核线程 进行调度,将线程任务映射到各个CPU上。那么,这样的内核就叫多线程内核

    2. 其次,从程序来说:应用程序不能直接使用内核线程,只能去使用内核线程的一种高级接口 - 轻量级进程(LWP) 。对于编程语言来说,它们操作的实际上是这个LWP,LWP就是此时就可以被叫线程

    3. 最后,轻量级进程线程 之间的映射关系是1:1的,所以,这种方式叫做一对一线程模型


3. Java线程调度

线程调度是指系统为线程分配处理器使用权的过程,主要的调度方式有两种:

  1. 协同式线程调度(Cooperative Threads-Scheduling)
    这种方式是原始方式,由一个线程执行完通知另一个线程。已经很少使用,很容易造成阻塞。

  2. 抢占式线程调度(Preemptive Threads-Scheduling)
    主流方式,由系统来根据一系列复杂的规则为每个线程分配执行时间,线程的切换不是由线程自己做主(Java可以有Thread.yield()来让出执行时间,但是没有获取执行时间的方式)。

虽然抢占式调度是系统自动完成的,但是我们可以给出“建议”,即通过设置线程的优先 - priority

  1. Java语言有10个级别的线程优先级,其实在代码中就是1 - 10 十个int常量(默认5),通过setPriority(int x)来设置。

  2. 但是不要依赖优先级,因为它并不靠谱,最终结果仍取决于OS。仅举一例,比如Java有10种优先级,而Windows只提供了7种,那它们怎么可能一一对应呢?

所以,不要太依赖优先级。

请注意,抢占式调度总体来说是绝对由OS内核来决定的,程序只能使用“祈使语句”,即使是放弃使用CPU。
比如,Thread.yield(int x);中这么说:
A hint to the scheduler that the current thread is willing to yield its current use of a processor. The scheduler is free to ignore this hint.


4. 线程状态及状体转换

1. Java语言规范定义了如下6种线程状态:

  1. New - 新建
    创建后,尚未启动的线程

  2. Runable - 可运行

    1. 正在JVM中执行的线程处于这种状态(注意不是在CPU中执行,见2)
    2. Runable对应了两种OS线程状态Running和Ready,也就说Runable状态的Java线程有可能正在执行,也有可能正在等待CPU为它分配执行时间
  3. Blocked - 阻塞的
    其它线程占有的监视器的锁,此线程等待着进入同步区域的时候(等待获取锁),线程处于这种状态

  4. Waiting - 无限期等待
    处于监视器所属对象的wait set中,等待着被其它(此对象监视器所有者)线程唤醒。调用如下方法会时当前线程进入Waiting状态:

    • java.lang.Object#wait();
    • threadObj.join()/threadObj.join(0)方法
    • LockSupport.park()方法
  5. Timed Waiting - 期限等待
    在一定时间内会被系统自动唤醒,当然也会被唤醒。调用如下方法会让线程进入期限等待状态:

    • java.lang.Object#wait(long);
    • java.lang.Thread#sleep(long);
    • threadObj.join(n)方法
    • LockSupport.parkNanos(obj ,n)方法
    • LockSupport.parkUntil(obj ,n)方法
  6. Terminated
    已经终止的线程处于这种状态

2. 状态转换如下

这里写图片描述

可以通过线程对象的Thread.getState();方法来获取线程当前状态。


《Java线程和线程同步 - 线程(2)》
《Java线程模型、线程状态 - 线程(3)》


参考文献:

[ 1 ] 周志明.深入理解Java虚拟机[M].第2版.北京:机械工业出版社,2015.8.
[ 2 ] James Gosling,Bill Joy,Guy Steele,Gilad Bracha,Alex Buckley.The Java® Language Specification . Java SE 8 Edition . 英文版[EB/OL].2015-02-13.

阅读更多

没有更多推荐了,返回首页