为什么 Java 线程没有 Running 状态?
什么是 RUNNABLE?
直接看它的 Javadoc 中的说明:
一个在 JVM 中执行的线程处于这一状态中。(A threadexecuting in the Java virtual machine is in this state.)
而传统的进(线)程状态一般划分如下:
与传统的ready状态的区别
更具体点,javadoc 中是这样说的:
处于 runnable 状态下的线程正在 Java 虚拟机中执行,但它可能正在等待来自于操作系统的其它资源,比如处理器。
A thread in the runnable state is executing in the Java virtual machine but it may be waiting forother resources from the operating system such as processor.
显然,runnable 状态实质上是包括了 ready 状态的。
甚至还可能有包括上图中的 waiting 状态的部分细分状态,在后面我们将会看到这一点。
与传统的running状态的区别
更复杂的可能还会加入优先级(priority)的机制。
这个时间分片通常是很小的,一个线程一次最多只能在 cpu 上运行比如10-20ms 的时间(此时处于 running 状态),也即大概只有0.01秒这一量级,时间片用后就要被切换下来放入调度队列的末尾等待再次调度。(也即回到 ready 状态)
注:如果期间进行了 I/O 的操作还会导致提前释放时间分片,并进入等待队列。 又或者是时间分片没有用完就被抢占,这时也是回到 ready 状态。
不计切换开销(每次在1ms 以内),相当于1秒内有50-100次切换。事实上时间片经常没用完,线程就因为各种原因被中断,实际发生的切换次数还会更多。
时间分片也是可配置的,如果不追求在多个线程间很快的响应,也可以把这个时间配置得大一点,以减少切换带来的开销。
如果是多核CPU,才有可能实现真正意义上的并发,这种情况通常也叫并行(pararell),不过你可能也会看到这两词会被混着用,这里就不去纠结它们的区别了。
当你看到监控上显示是 running 时,对应的线程可能早就被切换下去了,甚至又再次地切换了上来,也许你只能看到 ready 与 running 两个状态在快速地闪烁。
当然,对于精确的性能评估而言,获得准确的 running 时间是有必要的。
我们将看到,Java 线程状态的改变通常只与自身显式引入的机制有关。
当I/O阻塞时
与 cpu 正打得火热的线程可能不情愿听到这一中断信号,因为它意味着这一次与 cpu 缠绵的时间又要到头了……奴为出来难,何日君再来?
@Test
public void testInBlockedIOState() throws InterruptedException {
Scanner in = new Scanner(System.in);
// 创建一个名为“输入输出”的线程t
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
// 命令行中的阻塞读
String input = in.nextLine();
System.out.println(input);
} catch (Exception e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(in);
}
}
}, "输入输出"); // 线程的名字
// 启动
t.start();
// 确保run已经得到执行
Thread.sleep(100);
// 状态为RUNNABLE
assertThat(t.getState()).isEqualTo(Thread.State.RUNNABLE);
}
public void testBlockedSocketState() throws Exception {
Thread serverThread = new Thread(new Runnable() {
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(10086);
while (true) {
// 阻塞的accept方法
Socket socket = serverSocket.accept();
// TODO
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}, "socket线程"); // 线程的名字
serverThread.start();
// 确保run已经得到执行
Thread.sleep(500);
// 状态为RUNNABLE
assertThat(serverThread.getState()).isEqualTo(Thread.State.RUNNABLE);
}
至少我们看到了,进行传统上的 IO 操作时,口语上我们也会说“阻塞”,但这个“阻塞”与线程的 BLOCKED 状态是两码事!
如何看待RUNNABLE状态?
JVM 并不关心底层的实现细节,什么时间分片也好,什么 IO 时就要切换也好,它并不关心。
你用嘴,用手,还是用什么鸟东西来满足它的需求,它并不关心~
就好比前台或保安坐在他们的位置上,可能没有接待什么人,但你能说他们没在工作吗?
比如 synchronize 机制有可能让线程进入BLOCKED 状态,sleep,wait等方法则可能让其进入 WATING 之类的状态。
RUNNABLE 状态对应了传统的 ready, running 以及部分的 waiting 状态。
作者:国栋,来源:http://rrd.me/ekN5T
- END -推荐阅读:1、厉害了,淘宝千万并发,14 次架构演进…关注Java技术栈公众号在后台回复:Java,可获取一份栈长整理的最新Java 技术干货。点击「阅读原文」和栈长学更多~