Thread thread status and common methods

thread state

In the Thread class, the thread state is realized through threadStatusattributes and enumeration classes:State

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;
    }

/**
     * Returns the state of this thread.
     * This method is designed for use in monitoring of the system state,
     * not for synchronization control.
     *
     * @return this thread's state.
     * @since 1.5
     */
    public State getState() {
        // get current thread state
        // Android-changed: Replace unused threadStatus field with started field.
        // Use Android specific nativeGetStatus() method. See comment on started field for more
        // information.
        // return sun.misc.VM.toThreadState(threadStatus);
        return State.values()[nativeGetStatus(started)];
    }


It can be seen from the source code that the thread has a total of 6 states, and the state transition relationship is shown in the following figure:

image.png

As can be seen from the definition of the state, the RUNNABLE state includes what we usually call runningand readytwo states.

common method

currentThread

The currentThread in the source code is defined as follows:

  /**
     * Returns a reference to the currently executing thread object.
     *
     * @return  the currently executing thread.
     */
    @FastNative
    public static native Thread currentThread();


It can be seen that it is a static method and a native method that returns the currently executing thread.
Here, 当前正在执行的线程refers to 当前正在执行这段代码的线程.
We know that a thread is the smallest unit of CPU scheduling, and any piece of code must always be executed by a thread, so this method returns the Thread.currentThreadthread that is executing this line of code, for example:

class ThreadName {

    companion object{
        @JvmStatic
        fun main(args: Array<String>) {
            println(Thread.currentThread().name)
        }
    }
}

output:

main


sleep

When it comes to the sleep method, the two most frequently asked questions are:
1. What is the difference between Thread.sleep() and Thread.currentThread().sleep()?

  1. What is the difference between Thread.sleep() and Object.wait()?
    Let's look directly at the source code:
/**
     * Causes the currently executing thread to sleep (temporarily cease
     * execution) for the specified number of milliseconds, subject to
     * the precision and accuracy of system timers and schedulers. The thread
     * does not lose ownership of any monitors.
     *
     * @param  millis
     *         the length of time to sleep in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    // BEGIN Android-changed: Implement sleep() methods using a shared native implementation.
    public static void sleep(long millis) throws InterruptedException {
        sleep(millis, 0);
    }


It can be seen that the sleep method is also a static method. As Cause the currently executing thread to sleepcan be seen from the comments, it acts on 当前正在执行的线程, so we can answer the above question:

Thread.sleep() is no different from Thread.currentThread().sleep()

If there is any difference between them, it is that one uses the class to directly call the static method, and the other uses the class instance to call the static method.

In addition, there is a very important sentence in the above comment:

The thread does not lose ownership of any monitors.

That is, though sleep 函数使得当前线程让出了CPU,但是,当前线程仍然持有它所获得的监视器锁,这与同时让出CPU资源和监视器锁资源的wait方法是不一样的.
There is another version of the sleep method:

/**
 * Causes the currently executing thread to sleep (temporarily cease
 * execution) for the specified number of milliseconds plus the specified
 * number of nanoseconds, subject to the precision and accuracy of system
 * timers and schedulers. The thread does not lose ownership of any
 * monitors.
 *
 * @param  millis
 *         the length of time to sleep in milliseconds
 *
 * @param  nanos
 *         {@code 0-999999} additional nanoseconds to sleep
 *
 * @throws  IllegalArgumentException
 *          if the value of {@code millis} is negative, or the value of
 *          {@code nanos} is not in the range {@code 0-999999}
 *
 * @throws  InterruptedException
 *          if any thread has interrupted the current thread. The
 *          <i>interrupted status</i> of the current thread is
 *          cleared when this exception is thrown.
 */
public static void sleep(long millis, int nanos) throws InterruptedException {
    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException("nanosecond timeout value out of range");
    }

    if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
        millis++;
    }

    sleep(millis);
}

This method adds an extra nanosecond-level delay parameter, but we can see from the source code that this extra nanosecond-level delay parameter is of no use. In the end, the function still calls the above single-parameter sleep method, and the delay is still millisecond level. The extra parameter is to increase the delay of the current millisecond level by 1 millisecond at most.

In addition, it is worth mentioning that wait has wait()a method without parameters, which it calls wait(0)means to wait indefinitely, and sleep does not have a version without parameters, so sleep(0)what does it mean?

This point is not mentioned in the source code, but by guessing the definition of the sleep method, we know that it gives up the CPU for 0 milliseconds, which sounds meaningless, but in fact the current thread that calls Thread.sleep(0) is indeed blocked "Freeze" a bit to give other threads a chance to execute first. That is to say, the current thread will release some unused time slices for other threads or processes to use, which is equivalent to an action of giving way.

yield

The sleep(0) method was mentioned above, so we have to mention the yield method:

 /**
     * A hint to the scheduler that the current thread is willing to yield
     * its current use of a processor. The scheduler is free to ignore this
     * hint.
     *
     * <p> Yield is a heuristic attempt to improve relative progression
     * between threads that would otherwise over-utilise a CPU. Its use
     * should be combined with detailed profiling and benchmarking to
     * ensure that it actually has the desired effect.
     *
     * <p> It is rarely appropriate to use this method. It may be useful
     * for debugging or testing purposes, where it may help to reproduce
     * bugs due to race conditions. It may also be useful when designing
     * concurrency control constructs such as the ones in the
     * {@link java.util.concurrent.locks} package.
     */
    public static native void yield();


The yield method is also a native method. From its comments, it can be seen A hint to the scheduler that the current thread is willing to yield its current use of a processor. The scheduler is free to ignore this hint.that it is just a suggestion for the CPU, telling the CPU that the current thread is willing to give up the CPU to other threads. As for whether the CPU adopts it, it depends on the behavior of different manufacturers. A thread just yielded the CPU, and then immediately got the CPU. In contrast, the sleep method must give up CPU resources and sleep for a specified time without participating in CPU competition.

So calling the yield method will not cause the thread to exit RUNNABLEthe state, at most it will change the thread from running to ready, but the sleep method may change the thread state to becomeTIMED_WAITING

isAlive

isAliveThe method is used to check whether the thread is still alive. It is a native method, but it is not a static method, which means it must be called by the instance of the thread.

In fact, you can think about why it is not a static method, because static methods generally act on it. 当前正在执行的线程Since it is "currently executing", it must be Alive, so it is meaningless to call it as a static method.

/**
 * Tests if this thread is alive. A thread is alive if it has
 * been started and has not yet died.
 *
 * @return  <code>true</code> if this thread is alive;
 *          <code>false</code> otherwise.
 */
public final native boolean isAlive();


join

The join method is another one that converts a thread to WAITINGor TIMED_WATING, like the wait method, it has three versions,


 /**
     * Waits at most {@code millis} milliseconds for this thread to
     * die. A timeout of {@code 0} means to wait forever.
     *
     * <p> This implementation uses a loop of {@code this.wait} calls
     * conditioned on {@code this.isAlive}. As a thread terminates the
     * {@code this.notifyAll} method is invoked. It is recommended that
     * applications not use {@code wait}, {@code notify}, or
     * {@code notifyAll} on {@code Thread} instances.
     *
     * @param  millis
     *         the time to wait in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    // BEGIN Android-changed: Synchronize on separate lock object not this Thread.
    // public final synchronized void join(long millis)
    public final void join(long millis)
    throws InterruptedException {
        synchronized(lock) {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                lock.wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                lock.wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
        }
    }

The beginning of this source code comment tells us the role of the join method:

Waits at most {@code millis} milliseconds for this thread to die. A timeout of {@code 0} means to wait forever.

That is, the method waits for this threadtermination, up to the specified time, or if the specified time is 0, it waits forever.

There are two issues that need to be cleared up here:
1. Who is waiting this threadfor termination?
2. this threadWhich thread is it referring to?

For illustration, let's look at an example:

class JoinThreadTest {

    companion object{
        fun printWithThread(content:String){
            println("["+Thread.currentThread().name+"线程]: "+content)
        }


        var myThread=object :Thread(){
            override fun run() {
                super.run()
                printWithThread("我在自定义的线程的run方法里")
                printWithThread("我马上要休息1秒钟,并让出CPU给别的线程使用")

                try {
                    sleep(1000)
                    printWithThread("已经休息了1秒,又重新获得了CPU")
                    printWithThread("我休息好了,马上就退出了")
                }catch (e:Exception){
                    e.printStackTrace()
                }
            }
        }


        @JvmStatic
        fun main(args: Array<String>) {
            printWithThread("开始执行main方法")

            myThread.start()
            printWithThread("我在main方法里,我要等下面这个线程执行完了才能继续往下执行")
            myThread.join()
            printWithThread("我在main方法里面,马上就要退出了。")
        }
    }
}


In the above example, we called it in the main method myThread.join. Note that the above code has two threads, one is the thread that executes the main method, and the other is our custom myThreadthread, so the answers to the above two questions are:
1. The main thread is waiting this threadfor termination because we called it in the main method . myThread.join()
2. this threadThe thread refers to myThreadthe thread because we called the join method on the myThread object.

The execution result of the above code is:

[main线程]: 开始执行main方法
[main线程]: 我在main方法里,我要等下面这个线程执行完了才能继续往下执行
[Thread-2线程]: 我在自定义的线程的run方法里
[Thread-2线程]: 我马上要休息1秒钟,并让出CPU给别的线程使用
[Thread-2线程]: 已经休息了1秒,又重新获得了CPU
[Thread-2线程]: 我休息好了,马上就退出了
[main线程]: 我在main方法里面,马上就要退出了。

It can be seen from the running results that although the myThread thread gives up the CPU in the middle, the main thread still has to wait until its execution is completed before continuing to execute. Let's modify the code now so that the main thread waits for 0.5 seconds at most, which will be changed to myThread.join(); myThread.join(500)then The result is as follows:

[main线程]: 开始执行main方法
[main线程]: 我在main方法里,我要等下面这个线程执行完了才能继续往下执行
[Thread-2线程]: 我在自定义的线程的run方法里
[Thread-2线程]: 我马上要休息1秒钟,并让出CPU给别的线程使用
[main线程]: 我在main方法里面,马上就要退出了。
[Thread-2线程]: 已经休息了1秒,又重新获得了CPU
[Thread-2线程]: 我休息好了,马上就退出了


We see that since the main thread waits for myThread for 0.5 seconds at most, within one second of myThread sleeping, it does not wait and continues to execute, and then myThread seizes CPU resources and continues to run.

After having a perceptual cognition through the example, let's look at the source code again, first look at the join(0) part:

public final synchronized void join(long millis) throws InterruptedException {
    ...
    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        ...
    }
    ...
}

isAliveThis is a spin operation. Note that the method here wait(0)is the method of the thread instance. In the above example, it is myThreadthe method. Although Thread is a thread class, it is only special in its native method. In addition , it is an ordinary java class, and all classes in java inherit from classes Object, so the Thread class inherits the wait method of Object. myThreadAs an instance of the thread class, it naturally has a wait method.

We know that the execution of the wait method must obtain the monitor lock and must be called in a synchronous code block. Here we check the join method and find that it is indeed modified by keywords synchronizedand is a non-static method, so it uses the current object The instance's monitor lock (this).

Let's stroke it from beginning to end
1. First of all, we need to be clear that there are two threads involved here, one is the main thread, and the other is our custom myThread thread.
2. We called it in the main method myThread.join(), and the main method is executed by the main thread, so myThread.join()the "current thread" that executes this line of code is the main thread.
3. The join method is a synchronization method that uses the object lock (this lock), which is the monitor object associated with the myThread object.
4. The main thread must first obtain the monitor lock of the join method before entering the synchronization code block.
5. After the main thread enters the synchronization code block, it will first check myThreadwhether the thread is still alive. Note that isAlive here is the method of the myThread thread, which is to check Whether the myThread thread is still alive, not the current thread (the current thread is the thread that executes the isAlive method, that is, the main thread).
6. If the myThread thread is still alive, (the main thread) will wait indefinitely, and give up the monitor lock and enter WAITINGthe state.
7. When the main thread WAITINGwakes up from the state (through notify, notifyAll or false wakeup), it will continue to compete for the monitor lock. After successfully obtaining the monitor lock, it will resume from the place where wait was called and continue to run. Since the wait method is in the while loop, it will continue to check myThreadwhether the thread is alive, and if it is still not terminated, it will continue to hang and wait.
8. It can be seen that the only way to exit this "spin" state is myThreadto terminate the thread (or throw an interrupt exception).

Some people are going to ask: If no one calls notifyor notifyAllthere is no false wakeup state, then the main thread will always wait(0)be suspended by the method? Isn't there even myThreada chance to detect whether the thread is alive? Even if myThreadit is terminated, it cannot be exited.

On this point, the comments in the documentation explain:

As a thread terminates the {@code this.notifyAll} method is invoked.

We know that the monitor lock of the wait(0) method is the myThread object (this), and when myThread terminates execution, this.notifyAll will be called, so all threads waiting for this lock will be awakened, and the main thread is waiting in The thread on this monitor, so when myThread finishes running, the main thread will be woken up from the wait method.

Here I am making a long-winded sentence,一定要分清执行代码的线程和方法所属的线程类所代表的线程!

For example, in the example above:

  • myThread.join()When the method of the myThread object, but the implementation of this method is the main thread;
  • isAlive()It is the method of the myThread object, but the main thread executes this method, and this method detects whether the myThread thread is alive
  • wait(0)is the method of the myThread object, but it is the main thread that executes this method, which makes the main thread hang, but the main thread hangs on the monitor represented by the myThread object.

The most important thing here is to distinguish between "myThread object" and "myThread thread". The myThread object sometimes represents the myThread thread, such as the isAlivemethod of the myThread object, which detects whether the myThread thread it represents is or not, but in fact, most of the time, myThread The object is an ordinary java object. The methods of this object are usually executed by other threads (such as the main thread in the above). For our custom thread (such as the myThread thread above), it is usually executed by itself The method to execute is only runthe method passed in.

Going back to the above example, it can be seen from the above analysis that the join(0) method achieves a certain degree of thread synchronization, that is, the current thread can only continue when the thread represented by the thread object to which the join method belongs terminates execution. Execute down, otherwise it will hang and wait.

This point also shows that using join(0) is very dangerous, because if myThreadthe thread is suspended because it cannot obtain resources, and the main thread is waiting for myThreadthe thread to terminate, the thread will always stop there and cannot be terminated, so in the source code A time-limited version is available:

public final synchronized void join(long millis) throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;
        ...
        if (millis == 0) {
            ...
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

Different from waiting indefinitely, time-limited waiting only waits for the specified time. If the specified time is up, it will jump out of the loop directly. The wait method used is also a version of time-limited wait. After the time is up, the main thread will be automatically awakened .

Next, let's take a look at the other two versions of the join method:

public final void join() throws InterruptedException {
    join(0);
}

public final synchronized void join(long millis, int nanos) throws InterruptedException {

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException("nanosecond timeout value out of range");
    }

    if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
        millis++;
    }

    join(millis);
}

It can be seen that the other two versions finally call the first version we analyzed, which is very similar to the wait method and the sleep method. As for why the wait method and the join method both provide a parameterless method but the sleep method does not, I personally think it is For semantic consistency:

wait()And join()respectively and wait(0)and join(0)are equivalent, they all represent indefinite waiting, and sleep(0) does not represent indefinite waiting, so the sleep method does not have a parameterless form to prevent semantic confusion. Except for this point, the implementation of these three methods in the two-parameter version is similar.

In addition, the last point worth noting is that we joinonly call isAlivethe method in the method to detect whether the thread is alive, and do not start the thread myThread. myThread.join()Call before calling to myThread.start()let the thread run first, otherwise jointhe method isAlivewill exit immediately if it is found to be false, and myThreadthe thread will not be executed.

at last

If you want to become an architect or want to break through the 20-30K salary range, then don't be limited to coding and business, but you must be able to select models, expand, and improve programming thinking. In addition, a good career plan is also very important, and the habit of learning is very important, but the most important thing is to be able to persevere. Any plan that cannot be implemented consistently is empty talk.

If you have no direction, here I would like to share with you a set of "Advanced Notes on the Eight Major Modules of Android" written by the senior architect of Ali, to help you organize the messy, scattered and fragmented knowledge systematically, so that you can systematically and efficiently Master the various knowledge points of Android development.
insert image description here
Compared with the fragmented content we usually read, the knowledge points of this note are more systematic, easier to understand and remember, and are arranged strictly according to the knowledge system.

Full set of video materials:

1. Interview collection

insert image description here
2. Source code analysis collection
insert image description here

3. The collection of open source frameworks
insert image description here
welcomes everyone to support with one click and three links. If you need the information in the article, directly scan the CSDN official certification WeChat card at the end of the article to get it for free↓↓↓

Guess you like

Origin blog.csdn.net/YoungOne2333/article/details/129693236