多线程--04--线程状态(生命周期)

本文详细介绍了Java线程的五种操作系统状态和六种API状态,强调了RUNNABLE状态包含的操作系统状态,并通过实例解释了阻塞I/O时线程状态显示为RUNNABLE的原因。同时,分析了线程状态转换,特别是BLOCKED到RUNNABLE以及线程中断与锁的关系。
摘要由CSDN通过智能技术生成

一、从操作系统层面看线程状态--5种状态

【初始状态】仅是在语言层面创建了线程对象,还未与操作系统线程关联
【可运行状态】(就绪状态)指该线程已经被创建(与操作系统线程关联),可以由 CPU 调度执行
【运行状态】指获取了 CPU 时间片运行中的状态。(注意:只有运行状态才会使用CPU
                     当 CPU 时间片用完,会从【运行状态】转换至【可运行状态】,会导致线程的上下文切换
【休眠状态】
                     1、如果调用了阻塞 API,如读写文件,这时该线程实际不会用到 CPU,会导致线程上下文切换,进入【休眠状态】;
                     2、等 BIO 操作完毕,会由操作系统唤醒休眠的线程,转换至【可运行状态】
                     3、与【可运行状态】的区别是,对【阻塞状态】的线程来说只要它们一直不唤醒,调度器就一直不会考虑调度它们。(只有可运行状态的才会被cpu调度执行
【终止状态】表示线程已经执行完毕或线程异常,生命周期已经结束,不会再转换为其它状态。

二、从Java API 层面来描述线程状态--6种状态

1、线程是操作系统中一种概念,Java 对其进行了封装,Java 线程本质上就是操作系统的中线程,其状态与操作系统的状态大致相同,但还是存在一些区别。

2、Java 线程状态定义在 Thread.State 枚举中,分别为:NEW, RUNNABLE, BLOCKED, WAITING,TIMED_WAITING, TERMINATED。其中TIMED_WAITING与WAITING的区别是-->有时间的等待。

 3、java中的线程状态与操作系统线程状态不同的2点

  • Java 线程 RUNNABLE 状态包括了操作系统的可运行状态与运行状态。一个处于 RUNNABLE 状态 的Java 线程,在操作系统层面状态可能为运行状态,也可能为可运行状态,正在等待系统分配 CPU 使用权。
  • java的BLOCKED, WAITING,TIMED_WAITING状态,实际是对操作系统中休眠状态的进一步细化

如下是Java 线程状态与操作系统线程状态映射关系:

 2.1 阻塞 I/O 会使线程休眠,为什么 Java 线程状态却是 RUNNABLE?

 使用 Java 阻塞 I/O 模型读取数据,将会导致线程阻塞,线程将会进入休眠,从而让出 CPU 的执行权,直到数据读取完成。这个期间如果使用 jstack 查看线程状态,却可以发现Java 线程状态是处于 RUNNABLE,这就和上面说的存在矛盾,为什么会这样?

(1)其实这是混淆了操作系统线程状态与 Java 线程状态。这里说的线程阻塞进入休眠状态,其实是操作系统层面线程实际状态。而我们使用 jstack 查看的线程状态却是 JVM 中的线程状态。

(2)当java调用阻塞式 API,线程进入休眠状态,这里指的是操作系统层面的。从 JVM 层面,Java 线程状态依然处于 RUNNABLE 状态。JVM 并不关心操作系统线程实际状态。从 JVM 看来等待 CPU 使用权(操作系统线程状态为可运行状态)与等待 I/O (操作系统线程状态处于休眠状态)没有区别,都是在等待某种资源,所以都归入 RUNNABLE 状态。

2.2 举个例子

2.2.1 例1--阻塞读时线程状态

(1)如下是连接服务端的代码,其中sc.nextLine()是阻塞读,会导致操作系统的线程状态进入休眠状态

public class SingleClient {
    public static void main(String[] args) {
        try {
            //1.创建Socket对象请求服务端的连接
            Socket socket = new Socket("127.0.0.1",9999);
            //2.从Socket对象中获取一个字节输出流
            OutputStream os = socket.getOutputStream();
            // 3.把字节输出流包装成一个打印流
            PrintStream ps = new PrintStream(os);

            /**
             * 生成一个随机名称,测试时使用
             */
            String  clientName = new Random().nextInt()+"";

            Scanner sc = new Scanner(System.in);
            while (true){
                System.out.print("请说:");
                //阻塞等待用户输入
                String msg = sc.nextLine();
                if ("end".equals(msg)){
                    sc.close();
                    break;
                }else{
                    ps.println(msg);
                    ps.flush();
                }

            }
            System.out.println("程序运行结束");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

(2)通过jconsole查看线程状态为Runnable

名称: main
状态: RUNNABLE
总阻止数: 0, 总等待数: 0

堆栈跟踪: 
java.io.FileInputStream.readBytes(Native Method)
java.io.FileInputStream.read(FileInputStream.java)
java.io.BufferedInputStream.read1(BufferedInputStream.java:284)
java.io.BufferedInputStream.read(BufferedInputStream.java:345)
   - 已锁定 java.io.BufferedInputStream@6784820c
sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
   - 已锁定 java.io.InputStreamReader@624a9164
java.io.InputStreamReader.read(InputStreamReader.java:184)
java.io.Reader.read(Reader.java:100)
java.util.Scanner.readInput(Scanner.java:804)
java.util.Scanner.findWithinHorizon(Scanner.java:1685)
java.util.Scanner.nextLine(Scanner.java:1538)
com.nation.bio.basic.client.SingleClient.main(SingleClient.java:17)

(3)通过jprofile查看线程状态,也为Runnable

2.2.1 例2-- jprofiler中网络IO的理解

三、java中线程状态转换

3.1 Runnable <------>Block

(1)首先来看最简单的 Blocked,从 Runnable 状态进入 Blocked 状态只有一种可能,就是进入 synchronized 保护的代码块时,没有抢到 monitor 锁进入阻塞队列中时,线程状态才会变为blocked。

(2)当处于 Blocked 的线程抢到 monitor 锁,就会从 Blocked 状态回到Runnable 状态。

注意:

(1)lock加锁/解锁本质是park/unpark,与synchronized不同。

(2)使用lock加锁,即使没获取到锁时进入等待队列中时,线程会由runnable变为wait或timeWait状态

3.2 线程其他状态转换

参考:03 线程是如何在 6 种状态之间转换的?.md

注意区分线程打断与锁打断:

(1)线程未加锁时调用interupt方法会打断线程;

(2)线程使用synchronized锁申请资源未申请到时会进入阻塞队列,进入阻塞队列的线程调用interupt方法不会被打断;

(3)线程使用lock.lock()方法申请资源未申请到时会进入阻塞队列,进入阻塞队列的线程调用interupt方法不会被打断;

(3)线程使用lock.lockInterruptibly()方法申请资源时,可以被打断,因为是在循环尝试获取锁,并未进入阻塞队列。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值