Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

State and transition of Java thread

2025-03-30 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > IT Information >

Share

Shulou(Shulou.com)11/24 Report--

This article comes from the official account of Wechat: low concurrency programming (ID:dibingfa). Author: flash.

Flash: what's the matter with you, Xiao Yu? I think you look very pale.

Xiao Yu: I went to the interview today, and the interviewer asked me about the status of the Java thread and its transformation.

Flash: Oh, it's a common interview question. Isn't there a status flow chart?

Xiao Yu: I know, but every time I have an interview, the flow chart I keep in mind becomes like this.

Flash: ha.

Xiao Yu: you still laugh. I'm so angry. Can you tell me about these messy conditions?

Flash: no problem, it's still the old rule. Forget all the states first and listen to me from the beginning!

Xiao Yu: OK.

The essence of thread state first you have to understand, when we talk about the state of a thread, what are we talking about?

Yes, it's just the value of a variable.

Which variable?

A variable in the Thread class, called

Private volatile int threadStatus = 0

This value is an integer that is not easy to understand and can be converted into an enumerated class through a mapping relationship (VM.toThreadState).

Public enum State {

NEW

RUNNABLE

BLOCKED

WAITING

TIMED_WAITING

TERMINATED

}

So let's just keep an eye on the change in the value of threadStatus.

It's that simple.

NEW now we don't have any objects of the Thread class, so there is no thread state.

The starting point for everything is to start by creating an object of the Thread class.

Thread t = new Thread ()

Of course, you can take a lot of parameters later.

Thread t = new Thread (r, "name1")

You can also new a subclass that inherits the Thread class.

Thread t = new MyThread ()

Why don't new have threads in the thread pool? New also came out of the house.

Public class Executors {static class DefaultThreadFactory implements ThreadFactory {public Thread newThread (Runnable r) {Thread t = newThread (); return t;} always calls the constructor of the Thread class at the beginning of everything.

This constructor will eventually call the init () method of the Thread class.

Private void init (ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) {this.grout = g; this.name = name; tid = nextThreadID ();} this init method simply appends a value to the properties in the object of the Thread class, and does nothing else.

It does not assign a value to theadStatus again, so its value is still its default value.

And the corresponding state of this value is STATE.NEW, which is called the initial state if it has to be translated into Chinese.

So having said so much, it is actually analyzed that to create a new object of the Thread class is to create a new thread, and the state of the thread at this time is NEW.

After the analysis, the integer value of threadStatus will be weakened, that is to say, it has changed its thread state directly, and we all know that it just changes the value of threadStatus.

RUNNABLE, what state does a thread that has just been in the NEW state correspond to in the operating system?

You didn't take a closer look at my above analysis.

Thread t = new Thread ()

I just made some superficial efforts to attach a value to the properties of one of my objects at the Java language level, and I didn't encounter anything at the operating system level at all.

So this NEW state, whether deep or shallow, is really just a boring enumeration value.

Below, the wonderful story has just begun.

The Thread object, lying idle in heap memory, comes to life after calling the start () method.

T.start ()

As soon as this method is called, it is awesome and will eventually be called into a pesky native method.

Private native void start0 (); it seems that changing the state is not as simple as a threadStatus = xxx, but that there is a local way to change it.

After nine bends and eighteen bends follow up the jvm source code, this method is called.

Hotspot/src/os/linux/vm/os_linux.cpppthread_create (); the famous unix method for creating threads, pthread_create.

At this point, a real thread is created in the operating system kernel.

And the linux operating system, there is no such thing as a newly created but not started thread, which starts running as soon as it is created.

Although the change in thread state cannot be detected from the source code, through debug, we can see that after calling the Thread.start () method, the state of the thread becomes RUNNABLE, running state.

Then our state diagrams have been enriched again.

Through this part, we know the following points:

1. After Java calls start (), a thread actually appears in the operating system and runs immediately.

2. Threads in Java have an one-to-one relationship with threads in the operating system kernel.

3. After calling start, the thread state changes to RUNNABLE, which is caused by some part of the code in the native method.

RUNNING and READYCPU have a core that can run only one thread at a time.

Which thread to execute depends on the scheduling mechanism of the operating system.

So, the above RUNNABLE status, to be exact, gives you the opportunity to be ready to run.

Threads in this state are also divided into threads running in CPU and a bunch of threads in ready waiting for CPU to allocate time slices to run.

Threads in ready are stored in a ready queue, waiting to be selected by the operating system's scheduling mechanism to run in CPU.

Note, of course, that the RUNNING and READY states here are created by ourselves for ease of description.

Neither the Java language nor the operating system distinguishes between these two states, and they are all called RUNNABLE in Java.

TERMINATED when a thread finishes executing (or calls a stop method that is no longer recommended), the thread's state changes to TERMINATED.

At this point, the thread can no longer be revived. If you force the start method at this time, an error will be reported.

Java.lang.IllegalThreadStateException

It's simple, because the first line of the start method is written locally.

Public synchronized void start () {if (threadStatus! = 0) throw new IllegalThreadStateException ();...}, what happens if you forcibly change threadStatus to 0 at this time? You can try.

BLOCKED has finished with the most common and simplest thread life cycle.

Initial-run-stop

There are no obstacles.

Next, it's a little more complicated, and we let the thread run into some obstacles.

First create an object lock.

Public static final Object lock = new Object (); A thread executes a sychronized block, the lock object is lock, and holds the lock all the time.

New Thread (()-{synchronized (lock) {while (true) {}) .start (); another thread also executes a sychronized block whose lock object is lock.

New Thread (()-{synchronized (lock) {...}}) .start (); then, when entering the synchronized block, the thread state changes to BLOCKED because the lock cannot be obtained.

The same is true for the synchronized method.

When the thread acquires the lock, it can enter the synchronized block, and the thread state changes to RUNNABLE.

So we get the following transformation relationship.

Of course, this is just a change in the state of the thread, and some substantial changes have taken place in the thread.

We do not consider the extreme optimization of synchronized by virtual machines.

When a synchronized block or method is entered and the lock is not acquired, the thread enters a synchronization queue for the lock object.

When the thread that holds the lock releases the lock, it wakes up all threads in the lock object synchronization queue, and these threads continue to try to grab the lock. Back and forth.

For example, there is a lock object A that thread 1 holds at this time. Threads 2, 3, and 4 failed to grab the lock respectively.

Thread 1 releases the lock, thread 2, 3, 4 becomes RUNNABLE again, and continues to grab the lock if thread 3 grabs the lock at this time.

Back and forth.

This part of WAITING, which is the most complex and has the most test points in the interview, will be divided into three parts. After listening to me, you will find that these three parts have a lot in common, but they are no longer isolated knowledge points.

Wait/notify, we just added something to the synchronized block.

New Thread (()-{synchronized (lock) {... Lock.wait ();.}}) .start (); when the lock.wait () method is called, three things happen.

1. Release the lock object lock (implying that the lock must be acquired first)

two。 Thread state becomes WAITING

3. The thread enters the waiting queue for the lock object

When is the thread awakened, removed from the waiting queue, and returned to the RUNNABLE state from the WAITING state?

The notify / notifyAll method of the same object must be called by another thread.

New Thread (()-{synchronized (lock) {... Lock.notify ();...}}) .start ()

It's just that notify wakes up only one thread, while notifyAll wakes up all threads in the waiting queue.

However, it should be noted that the awakened thread is removed from the waiting queue and the state changes to RUNNABLE, but it still needs to grab the lock, and the lock grab is successful before it can return from the wait method and continue execution.

If it fails, it will be the same as the BLOCKED process in the previous section.

So our whole flow chart is now like this.

The main thread of join is written like this.

Public static void main (String [] args) {thread t = new Thread (); t.start (); t.join ();} when execution reaches t.join (), the main thread becomes WAITING, and it is not until thread t finishes that the main thread changes back to RUNNABLE state and continues to execute.

It looks like another thread join during the execution of the main thread, and the main thread does not continue until it is finished.

So we have two more items in our state diagram.

So how does join magically achieve all this? Is it also put in the waiting queue like wait?

Open the source code of Thread.join () and you will find it very simple.

/ / this is all the useful information about Thread.java// 's join without parameters, omitting the extra branch public synchronized void join () {while (isAlive ()) {wait ();}}, that is, his essence is still to execute the wait () method, and the lock object is the Thread t object itself.

Then going from RUNNABLE to WAITING is exactly the same as executing the wait () method.

So how do you get back to RUNNABLE from WAITING?

The main thread calls wait, which requires another thread notify. Do you need this child thread t to call t.notifyAll () before it ends?

The answer is no, there is only one possibility, after the thread t ends, jvm automatically calls t.notifyAll () without our program showing and writing.

Yeah, that's it.

How do you prove it? Hearsay doesn't work. I have to take off jvm's coat today.

Sure enough, the following code was found.

Hotspot/src/share/vm/runtime/thread.cppvoid JavaThread::exit (...) {... Ensure_join (this);...} static void ensure_join (JavaThread* thread) {. Lock.notify_all (thread);.} We see that the virtual machine executes an ensure_join method after the execution of a thread's method, and the name shows that it is specifically designed for join.

If you continue to follow up, you will find a key piece of code, lock.notify_all, which is proof that when a thread ends, it will automatically call its own notifyAll method.

So, in fact, join is wait, and the end of a thread is notifyAll. Now, is it clearer?

Park/unpark has the above mechanisms of wait and notify, so it's easy to understand below.

A thread calls the following method.

LockSupport.park ()

The thread state changes from RUNNABLE to WAITING,

Another thread calls

LockSupport.unpark (the thread that Thread just had)

The thread just now will return from WAITING to RUNNABLE

But in terms of thread state flow, it is the same as wait and notify.

In terms of implementation mechanism, they are even simpler.

1. Park and unpark do not need to acquire the lock in advance, or have nothing to do with the lock at all.

two。 There is no waiting queue to say that unpark will precisely wake up a certain thread.

3. There is no order requirement for park and unpark, so you can call unpark first.

With regard to the third point, it is related to the principle of park, which I will only briefly explain here.

The thread has a counter with an initial value of 0

Calling park is

If this value is 0, the thread is suspended and the state is changed to WAITING. If the value is 1, change the value to 0 and do nothing else.

Calling unpark is

Change this value to 1

Then I'll use three examples, and you'll basically understand.

/ / example 1LockSupport.unpark (Thread.currentThread ()); / / 1LockSupport.park (); / / 0System.out.println ("can run this far"); / / example 2LockSupport.unpark (Thread.currentThread ()); / / 1LockSupport.unpark (Thread.currentThread ()); / / 1LockSupport.park (); / / 0System.out.println ("can run this far"); / / example 3LockSupport.unpark (Thread.currentThread ()); / / 1LockSupport.unpark (Thread.currentThread ()) / / 1LockSupport.park (); / / 0LockSupport.park (); / / WAITINGSystem.out.println ("cannot run here"); park is very simple to use and is also the underlying layer of the lock implementation in JDK. Its JVM and operating system level principle is very complex, another day can find a special section to explain.

Now our state diagram can be updated again.

The TIMED_WAITING part couldn't be simpler. The above methods that cause the thread to become a WAITING state add a timeout parameter and become a way to turn the thread into a TIMED_WAITING state. We update the flowchart directly.

The only difference between these methods is that when you return RUNNABLE from TIMED_WAITING, you can return the RUNNABLE state not only in the previous way, but also through the timeout.

That's all.

And look, everyone.

Wait needs to acquire the lock, release it, and then wait for it to be notify.

Join is the encapsulation of wait.

Park needs to wait for unpark to wake up, or be granted wake-up permission by unpark in advance.

Is there a way to just let the thread hang and be awakened only by waiting for the timeout to run out?

This method is

Thread.sleep (long)

Let's add it to the picture, and this part is all.

Then add it to the global diagram.

Postscript the state of the Java thread, there are six

NEW

RUNNABLE

BLOCKED

WAITING

TIMED_WAITING

TERMINATED

The classical thread five-state model has five states.

Create

Ready

Execution

Blockage

Termination

Different implementers may have mergers and splits.

For example, Java unifies the readiness and execution in the five-state model into RUNNABLE, and subdivides blocking (that is, the state in which it is impossible to get a CPU opportunity) into BLOCKED, WAITING, and TIMED_WAITING. Here we do not evaluate the good or bad.

In other words, BLOCKED, WAITING, TIMED_WAITING these states, threads can not get the right to run CPU, you can call it suspend, block, sleep, wait, many articles, you will see that these words are not used back and forth so seriously.

Two more questions that you might be confused about.

Call lock in jdk's Lock interface, and if the lock is not acquired, the thread will hang. What is the state of the thread at this time?

How many students think that it should become BLOCKED just like synchronized can't get the lock?

However, if you take a closer look at my above article, there is a sentence mentioned that the implementation of locks in jdk is based on AQS, while the bottom layer of AQS uses park and unpark to suspend and wake up threads, so it should be changed to WAITING or TIMED_WAITING state.

When the blocking IO method is called, what state does the thread become?

For example, when socket programming, when calling blocking methods such as accept (), read (), what state is the thread in?

The answer is in the RUNNABLE state, but in fact the thread does not have the right to run because it is blocked at the operating system level and needs to wait until the IO is ready to become ready.

But at the Java level, JVM thinks that waiting for IO is the same as waiting for the executive power of CPU. That's what people think. I still don't discuss whether it's good or bad here. If you think it's uncomfortable, you can design your own language, so what do you think? there's nothing others can do about you.

For example, when I was asked to design a language, I thought that threads that could be scheduled and executed by CPU were dead. In this way, there must be a classic interview question in my language. why does Flash define runnable threads as dead?

OK, that's all for today's article.

This article is a little devoted. I found that I forgot the beginning of Xiao Yu.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

IT Information

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report