Thread线程状态以及常用方法

线程状态

在Thread类中,线程状态时通过threadStatus属性以及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)];
    }


从源码中可以看出,线程一共有6种状态,其状态转换关系如下图所示:

image.png

从状态的定义中可以看出,RUNNABLE状态包含了我们通常所说的runningready两种状态。

常用方法

currentThread

源码中currentThread定义如下:

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


可见,它是一个静态方法,并且是一个native方法,返回的是当前正在执行的线程。
这里,当前正在执行的线程 指的是当前正在执行这段代码的线程
我们知道,线程是CPU调度的最小单位,任意一段代码总得由一个线程执行,所以该方法返回的是正在执行Thread.currentThread这行代码的线程,例如:

class ThreadName {

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

输出:

main


sleep

谈起sleep方法,问的最多的两个问题就是:
1.Thread.sleep()与Thread.currentThread().sleep()有什么区别?

  1. Thread.sleep()和Object.wait()有什么区别?
    我们直接来看源码:
/**
     * 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);
    }


可见,sleep方法也是一个静态方法,从注释Cause the currently executing thread to sleep中可以看出,它作用于当前正在执行的线程,所以上面那个问题我们就能回答了:

Thread.sleep()与Thread.currentThread().sleep()没有区别

扫描二维码关注公众号,回复: 14706688 查看本文章

如果硬要说它们有什么区别的话,那就是一个是用类直接调用静态方法,一个是用类的实例调用静态方法。

另外,上面的注释中还有一句非常重要的话:

The thread does not lose ownership of any monitors.

也就是说,虽然sleep 函数使得当前线程让出了CPU,但是,当前线程仍然持有它所获得的监视器锁,这与同时让出CPU资源和监视器锁资源的wait方法是不一样的
sleep方法还有另外一个版本:

/**
 * 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);
}

这个方法多加了纳秒级别的延时参数,但是我们看源码就知道,这个多加的纳秒级别的延时参数并没有什么用,最终该函数还是调用了上面的单参数sleep方法,延时还是毫秒级别的。多出来的参数最多是让当前毫秒级别的延时增加1毫秒。

另外,值得一提的是,wait有无参的wait()方法,它调用的是wait(0),表示无限期等待,而sleep并没有无参数的版本,那么sleep(0)代表什么呢?

这一点在源码中并没有提及,但是通过猜测sleep方法的定义我们知道,它是让出CPU 0毫秒,这听上去好像没有什么意义,但其实调用Thread.sleep(0)的当前线程确实被"冻结"了一下,让其他线程有机会优先执行。也就是说当前线程会释放一些未用完的时间片给其他线程或进程使用,就相当于一个让位动作。

yield

上面谈到了sleep(0)方法,就不得不提yield方法了:

 /**
     * 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();


yield方法也是一个native方法,从它的注释可以看出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. 它对于CPU只是一个建议,告诉CPU,当前线程愿意让出CPU给其他线程使用,至于CPU采不采纳,取决于不同厂商的行为,有可能一个线程刚yield出CPU,然后又立马获得了CPU。与之相对对,sleep方法一定会让出CPU资源,并且休眠指定的时间,不参与CPU的竞争。

所以调用yield方法不会使线程退出RUNNABLE状态,顶多会使线程从running变成ready,但是sleep方法有可能将线程状态转为成TIMED_WAITING

isAlive

isAlive方法用于检查线程是否还活着,它是一个native方法,但不是静态方法,也就是说它必须被线程的实例所调用。

其实大家可以思考一下它为什么不是静态方法,因为静态方法一般都是作用于当前正在执行的线程,既然是"当前正在执行",那必然是Alive的,所以作为静态方法调用并没有意义。

/**
 * 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

join方法是另一个能将线程转换成WAITING或者TIMED_WATING的,它和wait方法一样,有三个版本,


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

这段源码注释的开头部分就告诉了我们join方法的作用:

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

也就是说,该方法等待this thread终止,最多等待指定的时间,如果指定时间为0,则一直等。

这里有两个问题需要弄清除:
1.谁在等this thread终止?
2.this thread指的是哪个线程?

为了便于说明,我们来看一个例子:

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方法里面,马上就要退出了。")
        }
    }
}


在上面的例子中,我们在main方法中调用了myThread.join,注意上面这段代码有两个线程,一个是执行main方法的线程,一个是我们自定义的myThread线程,所以上面的两个问题的答案是:
1.main线程在等this thread的终止,因为我们在main方法中调用了myThread.join()
2.this thread线程指的是myThread线程,因为我们在myThread对象上调用了join方法。

上面这段代码的执行结果为:

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

从运行结果可以看出,虽然myThread线程中途让出了CPU,main线程还是必须等到其执行完毕了才能继续往下执行,我们现在修改一下代码,让main线程最多等0.5秒,即将myThread.join()改为myThread.join(500);则结果如下:

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


我们看到,由于main线程最多等待myThread0.5秒,在myThread休眠的一秒内,它就不等了,继续往下执行,而随后myThread抢占到CPU资源继续运行。

通过例子有了感性的认知后,我们再来看下源码,首先看join(0)部分:

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

这是一个自旋操作,注意,这里的isAlivewait(0)方法都是线程实例的方法,在上面的例子中就是myThread的方法,Thread虽然是一个线程类,但只是特殊在它的native方法上,除此之外,它就是一个普通的java类,而java中所有类都继承自Object类,所以Thread类继承了Object的wait方法,myThread作为线程类的实例,自然也有wait方法。

我们知道,执行wait方法必须拿到监视器锁,并且必须在同步代码块中调用,这里我们检查join方法发现,它确实被synchronized关键字修饰,并且是一个非静态方法,所以它使用的是当前对象实例的监视器锁(this)。

我们从头到尾捋一捋
1.首先我们要明确,这里牵涉到两个线程,一个是main线程,一个是我们自定义的myThread线程。
2.我们在main方法中调用了myThread.join(),main方法由main线程执行,所以执行myThread.join()这行代码的"当前线程"是main线程。
3.join方法是一个同步方法,使用的是对象锁(this锁),即myThread对象所关联的监视器对象。
4.main线程必须首先拿到join方法的监视器锁才能进入同步代码块
5.main线程进入同步代码块后会首先检查myThread线程是否还存活,注意,这里的isAlive是myThread线程的方法,它是检查myThread线程是否还活着,而不是当前线程(当前线程是执行isAlive方法的线程,即main线程)。
6.如果myThread线程还存活,(main线程)就无限期等待,并让出监视器锁,进入WAITING状态。
7.当main线程从WAITING状态被唤醒后(通过notify,notifyAll或者假唤醒),将继续竞争监视器锁,当成功获得监视器锁后,他将从调用wait的地方恢复,继续运行。由于wait方法在while循环中,则它将继续检查myThread线程是否存活,如果还是没有中止,则继续挂起等待。
8.可以看出,退出这个"自旋"状态的唯一路径就是myThread线程终止运行(或者有中断异常抛出)。

有些人就要问了:要是没有人调用notify或者notifyAll,也没有假唤醒状态的发生,那main线程不就一直被wait(0)方法挂起了吗?这样以来不就连检测myThread线程是否存活的机会都没有吗?这样即使myThread终止了,也无法退出了啊。

关于这一点,文档中注释做了解释:

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

我们知道,wait(0)方法的监视器锁就是myThread对象(this),而当myThread终止执行时,this.notifyAll会被调用,所以所有等待this锁的线程都会被唤醒,而main线程就是等待在这个监视器上的线程,因此myThread运行结束时,main线程会从wait方法处被唤醒。

这里我在啰嗦一句,一定要分清执行代码的线程和方法所属的线程类所代表的线程!

例如,在上面的例子中:

  • myThread.join()时myThread对象的方法,但是执行这个方法的是main线程;
  • isAlive()是myThread对象的方法,但是执行这个方法的是main线程,而这个方法检测是myThread线程是否活着
  • wait(0)是myThread对象的方法,但是执行这个方法的是main线程,它使得main线程挂起,但是main线程是在myThread对象代表的monitor上挂起。

这里最重要的是区分"myThread对象"和"myThread线程",myThread对象有时候代表了myThread线程,列如myThread对象的isAlive方法,检测的就是它代表的myThread线程是否或者,但是其实大多数时候,myThread对象就是普通的java对象,这个对象的方法通常也都是由其他线程(例如上面中的main线程)来执行的,对于我们自定义的线程来说(例如上面的myThread线程),通常由它自己执行的方法就只有传入的run方法了。

在回到上面的例子,从上面的分析中可以看出,join(0)方法实现了一定程度上的线程同步,即当前线程只有等join方法所属的线程对象所代表的线程终止执行了才能继续往下执行,否则将一直挂起等待。

这一点也说明使用join(0)是很危险的,因为如果myThread线程因为得不到资源一直被挂起,而main线程又在等待myThread线程终止,则线程永远会停在那里,无法终止,所以源码中提供了限时等待的版本:

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

与无限期等待不同的是,限时等待只等待指定的时间,如果指定的时间到了就直接从循环中跳出来,使用的wait方法也是限时wait的版本,定时时间到了之后,main线程会被自动唤醒。

接下来我们来看看其他两个版本的join方法:

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

可见,其他两个版本最终调用的都是我们分析的第一版本,这和wait方法,sleep方法很像,至于为什么wait方法和join方法都提供了无参方法而sleep方法没有,我个人认为是为了保持语义的一致性:

wait()join()分别和wait(0)join(0)等价,他们都代表了无限期等待,而sleep(0)并不代表无限期等待,所以sleep方法没有无参的形式,以防止语义上的混乱。除了这点,这三个方法在两个参数的版本中的实现,都大同小异。

另外最后一点值得注意的是,我们在join方法中只调用了isAlive方法检测线程是否存活,并没有启动这个线程,也就是说,如果我们想要实现当前线程等待myThread线程执行完成之后在执行的效果,就必须在调用myThread.join()之前调用myThread.start()让线程先跑起来,否则join方法发现isAlive为false会立即退出,myThread线程就不会被执行。

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集

在这里插入图片描述
二、源码解析合集
在这里插入图片描述

三、开源框架合集
在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓

猜你喜欢

转载自blog.csdn.net/YoungOne2333/article/details/129693236
今日推荐