What Does Java’s RUNNABLE State Really Mean? A Deep Dive into Thread States
This article explains the distinction between Java's RUNNABLE thread state and the operating‑system concepts of ready, running, and waiting, covering JVM‑level definitions, I/O blocking behavior, time‑slice scheduling, and includes concrete code examples to illustrate how blocked I/O still appears as RUNNABLE.
What is RUNNABLE?
The Javadoc defines RUNNABLE as a thread that is executing in the Java virtual machine.
A thread executing in the Java virtual machine is in this state.
Traditional OS thread states are usually divided as shown below:
Difference from the traditional READY state
According to the Javadoc, a thread in the RUNNABLE state is executing in the JVM but it may be waiting for other OS resources such as the processor.
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.
Thus RUNNABLE actually includes the READY state.
Difference from the traditional RUNNING state
Java thread states do not have a separate RUNNING state; the RUNNABLE state already covers it.
Modern OSes use time‑slice (time quantum) pre‑emptive round‑robin scheduling. A thread may run for 10‑20 ms (the RUNNING period) before being pre‑empted and placed back into the READY queue.
Context switches save the thread’s execution state to memory for later resumption.
When I/O is blocked
Traditional blocking I/O makes the CPU wait, but the thread is moved to a waiting queue (the WAITING state in the diagram).
When the I/O completes, an interrupt notifies the CPU, the blocked thread is interrupted, returns to the READY queue, and may be scheduled again.
Code examples
@Test
public void testInBlockedIOState() throws InterruptedException {
Scanner in = new Scanner(System.in);
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();
Thread.sleep(100);
// state should be RUNNABLE
assertThat(t.getState()).isEqualTo(Thread.State.RUNNABLE);
}Running this test shows the thread remains in RUNNABLE even during a blocking read.
@Test
public void testBlockedSocketState() throws Exception {
Thread serverThread = new Thread(new Runnable() {
@Override
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(10086);
while (true) {
// blocking accept()
Socket socket = serverSocket.accept();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); }
}
}
}, "socket线程");
serverThread.start();
Thread.sleep(500);
// state should be RUNNABLE
assertThat(serverThread.getState()).isEqualTo(Thread.State.RUNNABLE);
}Similarly, a thread blocked on ServerSocket.accept() is reported as RUNNABLE.
How to view the RUNNABLE state
There are two layers: the JVM layer and the underlying OS layer. The JVM maps OS threads to Java threads but only cares about the JVM‑level state.
The JVM treats a thread as still executing even if the OS thread is blocked, because the JVM’s notion of execution includes waiting for any resource.
Mapping of Java thread states
RUNNABLE corresponds to the traditional READY, RUNNING, and part of the WAITING states.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
