Fundamentals 12 min read

Understanding the RUNNABLE Thread State in Java

This article explains the Java RUNNABLE thread state, how it differs from traditional OS ready/running states, its relationship with time‑slicing, blocking I/O, and why the JVM reports many blocked operations as RUNNABLE, illustrated with code examples and diagrams.

Java Captain
Java Captain
Java Captain
Understanding the RUNNABLE Thread State in Java

Java thread states are defined by the Thread.State enum inside the Thread class, which is separate from the operating system's thread states; the article begins by clarifying this distinction.

A thread executing in the Java virtual machine is in the RUNNABLE state.

The traditional OS view classifies threads as ready or running , and the article shows a diagram of these classic states.

Note: the term "process" here actually refers to early single‑threaded processes, so the process state is essentially a thread state.

RUNNABLE in the JVM encompasses both the ready state and, when the thread is actually using the CPU, the running state; it may also include parts of the waiting state when the thread is awaiting OS resources such as the processor.

Modern pre‑emptive, time‑sharing operating systems use a time‑quantum (time slice) to schedule threads in a round‑robin fashion, optionally with priority. A typical slice is 10‑20 ms, after which the thread is pre‑empted, placed at the end of the ready queue, and a context switch saves its execution state.

Context switches usually take less than 1 ms, allowing 50‑100 switches per second; on multi‑core CPUs true parallelism can be achieved.

When a thread performs a blocking I/O operation, the CPU stops executing that thread while the I/O device (e.g., disk) continues work; the thread is moved to a waiting queue and later re‑queued when the I/O completes via an interrupt‑driven mechanism.

The article emphasizes that although the OS thread may be in a blocked state, the JVM still reports the Java thread as RUNNABLE because the JVM abstracts away the underlying details.

Code examples demonstrate that a thread blocked on standard input or on ServerSocket.accept() remains in the Thread.State.RUNNABLE state, as verified by assertions and debugger screenshots.

@Test
public void testInBlockedIOState() throws InterruptedException {
    Scanner in = new Scanner(System.in);
    Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                // blocking read from console
                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);
}

Similarly, a server thread blocked on accept() is shown to stay in the RUNNABLE state after a short sleep.

@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();
                    // TODO
                }
            } 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);
}

The article notes that Java's newer NIO package is not examined here, but the key takeaway is that traditional blocking I/O appears as "blocked" to humans while the JVM still labels the thread as RUNNABLE.

At least we see that the colloquial "blocking" of I/O is not the same as the JVM's BLOCKED state.

Finally, the article presents a mapping diagram showing how the JVM's RUNNABLE state corresponds to the OS's ready, running, and parts of waiting states, concluding that the JVM abstracts away low‑level scheduling details and focuses on whether the thread is considered to be executing.

(End)

JavaJVMconcurrencythreadRunnable
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.