Java Scanner IO等待时,是什么线程状态?

今天我们来思考一个问题,对于下面这段代码1,想必大家一定很熟悉的,在执行到 scanner.next(); 时,程序会等待控制台进行输入,当输入了数据后,会对应有输出,那么问题是,scanner.next() 时,线程处于什么状态呢?

import java.util.Scanner;

public class ScannerExample {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String next = scanner.next();
        System.out.println(next);
    }
}

运行结果
在这里插入图片描述
JAVA线程状态一共有6种状态(JAVA语法层面),我们可以在Thread类中找到答案

public
class Thread implements Runnable {

    public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }
}

一开始思考这个问题时,我第一感觉应该是WAITING状态,理由是如果我控制台一直不输入,那线程岂不是一直在等待输入,直到有输入时,会由等待状态进入运行状态,那么真的是这样吗?(答案:不是)

为了让问题理解得更透彻,我们来看下下面这份代码2

import java.util.Scanner;

public class ScannerInterrupt {

    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            Scanner scanner = new Scanner(System.in);
            while (true) {
                String next = scanner.next();
                if (Thread.interrupted()) {
                    System.out.println("interrupted");
                    break;
                }
                System.out.println(next);
            }
        });
        t.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t.interrupt();
        System.out.println(t.getState());
        while(true) {
        }
    }
}

我是开启了一个线程t,在线程 t 中执行 scanner.next(); 并且主线程sleep 2s后,给线程 t 发送一个中断标记

如果 scanner.next(); 是进入等待状态,那么它应该是以下几种方式

  • Object#wait()
  • Thread#join()
  • LockSupport.park()
  • LockSupport.park(Object)

下面附上一张图,是线程6种状态的转移关系
关于线程状态的更多内容,可以见我的笔记 深入理解JAVA并发与集合/JAVA线程基础.md#线程的生命周期
在这里插入图片描述
上面4种状态下,当其他线程执行了 t.interrupt(),赋予了一个中断标记后,等待状态都会转变为Runnable状态,且有的会抛异常,有的不会

但是代码2的执行结果如下,并没有输出 interrupted,说明线程 t 没有被中断,那么就不是等待状态了

RUNNABLE

我们通过 jps 先查到进程ID,我这里是 14220 ScannerInterrupt

然后通过 jstack 命令,查到线程栈信息,这里只贴出部分

jstack 14220

"Thread-0" #11 prio=5 os_prio=0 tid=0x000000001eb5f000 nid=0xaac runnable [0x000000001f44e000]
   java.lang.Thread.State: RUNNABLE
        at java.io.FileInputStream.readBytes(Native Method)
        at java.io.FileInputStream.read(FileInputStream.java:255)
        at java.io.BufferedInputStream.read1(BufferedInputStream.java:284)
        at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
        - locked <0x000000076b05ae00> (a java.io.BufferedInputStream)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
        - locked <0x000000076b68f408> (a java.io.InputStreamReader)
        at java.io.InputStreamReader.read(InputStreamReader.java:184)
        at java.io.Reader.read(Reader.java:100)
        at java.util.Scanner.readInput(Scanner.java:804)
        at java.util.Scanner.next(Scanner.java:1369)
        at ioblocked.ScannerInterrupt.lambda$main$0(ScannerInterrupt.java:11)
        at ioblocked.ScannerInterrupt$$Lambda$1/1831932724.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

"main" #1 prio=5 os_prio=0 tid=0x0000000002bc3800 nid=0x37a4 runnable [0x000000000266f000]
   java.lang.Thread.State: RUNNABLE
        at ioblocked.ScannerInterrupt.main(ScannerInterrupt.java:27)

从线程栈信息中可看出,main线程是RUNNABLE状态,这个没有问题,它在while()循环中,可是线程 t 却也是RUNNABLE状态,这是为什么呢?

在JAVA线程状态中,RUNNABLE含义如下

Thread state for a runnable thread. A thread in the runnable state is executing in the Java virtual machine but it may be waiting for other resources from the operating system such as processor.

线程在JVM中执行,但它可能在等待一些操作系统的资源,这里的资源一般理解为CPU时间片

RUNNABLE状态在JVM底层对应两种状态:READY、RUNNING

在线程执行了 t.start() 后,它首先是到 READY 状态,此时还没有真正的运行,而当分配了CPU时间片后,才会进入到RUNNING状态

既然如此,我们可否再深入的查一下线程 t 在JVM底层对应的那些状态呢?

我下面使用的是 windows10系统(这里留一个坑:Linux上如何查询?)

使用一个叫 ProcessExplorer 的工具(网上可以下载),打开软件后大概是下面这样子的

我们找到14420这个进程ID
在这里插入图片描述
双击那一行,可以打开一个新窗口,点击Threads,可以看到具体的线程情况

根据上面的线程栈信息,main线程是 nid=0x37a4,线程 t 是 nid=0xaac,分别对应十进制数是 14244、2732

查看14244(main线程)的状态如下,发现是RUNNING状态
在这里插入图片描述
查看2732线程(线程t)的状态,发现是Wait:Executive状态(这里再留一个坑:Wait:XXX状态具体什么含义?)
在这里插入图片描述
至此,我们就清楚了,Java Scanner IO等待时,并不是想象中的Waiting状态,而是Runnable状态,但在JVM底层中是类似READY状态(Wait:Executive,等待执行),并没有分配CPU时间片,而当有控制台输入时,会为之分配CPU时间片

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值