Java线程状态及切换

点击上方蓝色字体,选择“标星公众号”
java

优质文章,第一时间送达web

  做者 |  血夜之末spring

来源 |  urlify.cn/UrAnymspringboot

66套java从入门到精通实战课程分享微信

1、什么是Java线程状态

在Java程序中,用于描述Java线程的六种状态:架构

  • 新建(NEW):当前线程,刚刚新建出来,还没有启动。app

  • 运行(RUNNABLE):当前线程,处于竞争CPU时间分片或已经得到CPU时间片的状态。less

  • 等待(WAITTING):当前线程,处于休眠,不参与CPU时间片竞争的状态。编辑器

  • 定时等待(TIMED_WAITTING):当前线程,处于定时休眠,暂时不参与CPU时间片竞争的状态。ide

  • 阻塞(BLOCKED):当前线程,处于阻塞,不参与CPU时间片竞争的状态。

  • 终止(TERMINATED):当前线程,处于最终中止的状态。

新建状态,只能进入运行状态。而终止状态没法再转为其余状态。

等待/定时等待与阻塞,差异就是后者须要一个事件信号(如其余线程放弃当前线程须要的排他锁),才能够进行状态切换。固然,强行关闭也是能够的。

Java线程的实现并不受JVM规范约束,故不一样虚拟机的实现,每每不一样。目前主流的HotSpot是将每一个Java线程直接映射到一个操做系统的原生线程,从而由操做系统完成一系列的线程调度

2、哪里看Java线程状态

查看Java线程状态,主要存在三种方式:

  • java.lang.Thread.State下能够直接看到Java的六种线程状态

  • Java运行时,程序内部能够经过Thread.getState()获取目标线程状态

  • Java运行时,程序外部能够经过jstack等工具,查看线程状态

有关jstack等工具等使用,后续会有博客,专门阐述。

3、何时变换Java线程状态

Java线程状态的切换嘛。不啰嗦,直接上图。

这张图涵盖了Java线程状态切换的各种方法。相较网上一些图片,更为详尽一些。
若是有所遗漏,能够告诉我,我会及时填补上。

4、谁在使用Java线程状态

平常开发中,咱们并不会直接与线程状态进行交互。
咱们每每直接使用JDK包装好的工具,如JUC包下的各种工具等。

举个栗子

线程池中的应用

位置:com.sun.corba.se.impl.orbutil.threadpool.ThreadPoolImpl#close


    // Note that this method should not return until AFTER all threads have died.
    public void close() throws IOException {

        // Copy to avoid concurrent modification problems.
        List<WorkerThread> copy = null;
        synchronized (workersLock) {
            copy = new ArrayList<>(workers);
        }

        for (WorkerThread wt : copy) {
            wt.close();
            while (wt.getState() != Thread.State.TERMINATED) {
                try {
                    wt.join();
                } catch (InterruptedException exc) {
                    wrapper.interruptedJoinCallWhileClosingThreadPool(exc, wt, this);
                }
            }
        }

        threadGroup = null;
    }

实际查看JDK后发现,JDK中其实也没有辣么多的实例,而且大多数直接关联线程状态的,也是一些状态查看的东东。
这在文章开头就说,Java线程操做,是很底层的,甚至其实现都不包含在虚拟机规范中。
主流的HotSpot,也是直接将Java线程映射到系统线程,由系统进行一系列的线程调度处理。
因此,在JDK中,是直接对线程状态进行处理的部分是不多的。

5、为何须要线程状态

1.为何须要线程状态这一律念

这个问题,能够从两个角度来讲明:生命周期与资源管理

  • 一方面,线程状态很好地刻画了线程的整个生命周期,对生命周期中不一样阶段进行了有效划分。

  • 另外一方面,资源是有限的,需求是无限的。因此须要将系统资源有意识地进行调度,合理利用比较优点,追求帕累托最优。

实现后者的,就是利用线程在生命周期的不一样阶段这一自然属性带来的状态刻画,进行的分组。CPU调度只须要关注运行状态的线程。而阻塞,等待等线程,都有着属于本身的一套处理方式。最终得到资源(开发时对复杂性的应对,运行时对系统资源对消耗,应用者心智模型的成长等)的优化分配。

2.JDK中为何须要定义Java线程状态

前文有说到,Java中不多直接使用到线程状态。那么为何还要在JDK中定义Java的六种线程状态呢?
一方面,经过信息透明的方式,下降使用者使用时创建心智模型的成本。如,如今的咱们能够经过打印日志,打断点,快速了解Java的各个线程状态,并清楚了解产生的缘由。从而大大提升了咱们对Java线程的认识,进而更为愉快地拥抱JUC包下诸如线程池等工具。
另外一方面,经过能够直接应用的状态枚举,咱们能够很好地对现有工具进行二次开发等。如咱们能够经过扩展AQS,并在其中添加线程状态的校验,从而获得定制化的线程同步工具。

6、如何使用线程状态

使用的方式,我已经在上文说了:学习Java线程,定制线程相关工具开发。
这里给出一个有关线程学习的demo:


/**
 * @program: learning
 * @description: 用于确认线程状态问题
 * @author: Jarry
 * @create: 2020-10-26 22:25
 **/
public class ThreadState {

    public static void main(String[] args) {
        threadStateTest();
//        threadStateTest2();
//        threadStateWithBlocked();
//        threadStateWithException();
//        threadStateWithSuspend();
    }

    /**
     * 实践证实:Thread.suspend()与Thread.resume()不会改变线程状态
     * 线程状态该是Waiting,就Waiting。该Timed_Waiting就Timed_Waiting
     * Thread.suspend()与Thread.resume()只是挂起目标线程(而且不会释放锁资源)
     */
    private static void threadStateWithSuspend() {
        Thread thread1 = new Thread(() -> {
//            LockSupport.park();
            LockSupport.parkNanos(2000000000);
        });

        thread1.start();
        printThreadState(thread1);
        LockSupport.parkNanos(500000000);
        printThreadState(thread1);
        thread1.suspend();
        printThreadState(thread1);
        LockSupport.parkNanos(500000000);
        printThreadState(thread1);
        thread1.resume();
        LockSupport.parkNanos(500000000);
        printThreadState(thread1);

//        LockSupport.unpark(thread1);
    }

    /**
     * 展示线程阻塞状态
     */
    private static void threadStateWithBlocked() {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                synchronized (ThreadState.class) {
//                    LockSupport.parkNanos(2000000000);
                    LockSupport.park();
                }
            }
        };

        Thread thread1 = new Thread(runnable);
        Thread thread2 = new Thread(runnable);

        thread1.start();
        LockSupport.parkNanos(500000000);
        thread2.start();
        // 加上如下时间间隔,则结果:Runnable->Blocked
        // 推论:Thread.start()会将线程状态设置为Runnable,而后在遇到sync的锁,再切换为Blocked状态
//        LockSupport.parkNanos(500000000);

        printThreadState(thread2);
        LockSupport.parkNanos(500000000);
        printThreadState(thread2);

        LockSupport.parkNanos(500000000);
        LockSupport.unpark(thread1);
        LockSupport.unpark(thread2);

    }

    /**
     * 因为底层实现机制的不一样(相较于其余waiting的方法),没法直接进行Object.wait(),不然会抛出如下异常
     * @exception java.lang.IllegalMonitorStateException
     * object.wait()进行wait的方法,是直接对其wait_set进行操做
     */
    private static void threadStateWithException() {
        Thread thread1 = new Thread(() -> {
            try {
                ThreadState.class.wait(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });

        thread1.start();
        LockSupport.parkNanos(1000000000);
        printThreadState(thread1);

    }

    /**
     * Object.wait()的使用
     */
    private static void threadStateTest3() {
        Thread thread1 = new Thread(() -> {
            synchronized (ThreadState.class) {
                try {
                    ThreadState.class.wait(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        thread1.start();
        LockSupport.parkNanos(1000000000);
        printThreadState(thread1);

    }

    /**
     * 肯定LockSupport.parkNacos()是否能够生成Time_Waiting状态
     */
    private static void threadStateTest2() {
        Thread thread1 = new Thread(() -> {
            LockSupport.parkNanos(2000000000);
        });

        thread1.start();
        printThreadState(thread1);
        LockSupport.parkNanos(1000000000);

        printThreadState(thread1);
    }

    /**
     * 查看到除Blocked之外的全部线程状态
     */
    private static void threadStateTest() {
        Thread thread1 = new Thread(() -> {
            synchronized (ThreadState.class) {
                // Runnable
                printThreadState(Thread.currentThread());

                // 1.Thread.sleep(time)
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
                // 2.Object.wait(time)
//                try {
//                    ThreadState.class.wait(2000);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
                // 3.Thread.join(time)
//                try {
//                    Thread.currentThread().join(2000);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
                // 4.LockSupport.parkNanos(time);
//            LockSupport.parkNanos(2000000000);
                // 5.LockSupport.parkUntil(timeStamp);
//            LockSupport.parkUntil(System.currentTimeMillis()+2000);

                LockSupport.park();
            }

        });
        thread1.setName("test_thread");

        // New
        printThreadState(thread1);

        thread1.start();
        LockSupport.parkNanos(1000000000);
        // Timed_waiting
        printThreadState(thread1);

        LockSupport.parkNanos(2000000000);
        // Waiting
        printThreadState(thread1);

        LockSupport.unpark(thread1);

        LockSupport.parkNanos(1000000000);
        // Terminated
        printThreadState(thread1);


    }

    private static void printThreadState(Thread thread) {
        System.out.println("current Thread(" + thread.getName()+":" + thread.getId() + ") state:" + thread.getState());
    }

}

代码中有一些细碎的知识点,就不在这里赘述了。感兴趣的小伙伴,能够本身看看注释,自行验证。

7、扩展:系统状态(三态&五态)

操做系统就包含进程管理,做业管理,文件管理等。其中进程管理,就涉及系统状态的三态模型与五态模型。
其中,三态模型包含如下三种状态:

  • 就绪状态

  • 运行状态

  • 阻塞状态

而五态模型则包含如下五种状态:

  • 运行状态

  • 静止就绪态

  • 活跃就绪态

  • 静止阻塞态

  • 活跃阻塞态

具体状态切换等,能够看我以前写的一篇博客:
系统架构设计师-操做系统

最后,愿与诸君共进步。

8、附录

补充:WAITTING/TIMED_WAITTING与BLOCKED的区别

其实,我以前也挺纠结这个问题的。
当时的想法是WAITTING/TIMED_WAITTING是由JVM本身维持,而BLOCKED是由系统维持的。后面看到主流的HotSpot是将线程映射到系统原生线程的,因此这个想法大几率是错误的。
那么二者区别是什么呢?
直到我在上文的示例代码中,BLOCKED状态的线程,在其余线程释放其所需的锁时,该线程是先转为RUNNING状态,再变为其余状态。这引发个人注意。
最后在stackOverFlow的一篇文章(Difference between WAIT and BLOCKED thread states)中,看到这样的解释:

The important difference between the blocked and wait states is the impact on the scheduler. A thread in a blocked state is contending for a lock; that thread still counts as something the scheduler needs to service, possibly getting factored into the scheduler's decisions about how much time to give running threads (so that it can give the threads blocking on the lock a chance).
Once a thread is in the wait state the stress it puts on the system is minimized, and the scheduler doesn't have to worry about it. It goes dormant until it receives a notification. Except for the fact that it keeps an OS thread occupied it is entirely out of play.
This is why using notifyAll is less than ideal, it causes a bunch of threads that were previously happily dormant putting no load on the system to get woken up, where most of them will block until they can acquire the lock, find the condition they are waiting for is not true, and go back to waiting. It would be preferable to notify only those threads that have a chance of making progress.
(Using ReentrantLock instead of intrinsic locks allows you to have multiple conditions for one lock, so that you can make sure the notified thread is one that's waiting on a particular condition, avoiding the lost-notification bug in the case of a thread getting notified for something it can't act on.)

简单说,就是CPU时间片不会考虑WAITTING/TIMED_WAITTING状态。
可是,虽然BLOCKED状态的线程没法得到CPU时间片,可是系统调度时,依旧会考虑BLOCKED状态的线程,将其置于调度计算中。

若是哪位小伙伴对这方面有了解,但愿能够聊一聊。

参考

  • JDK

  • Java虚拟机规范

  • 《系统架构设计师教程》

  • 《深刻理解Java虚拟机》

  • 系统架构设计师-操做系统

  • java中的interrupt()方法

  • Lifecycle and States of a Thread in Java

  • Difference between WAIT and BLOCKED thread states






粉丝福利:实战springboot+CAS单点登陆系统视频教程免费领取

👇👇👇

   
👆长按上方微信二维码 2 秒
便可获取资料



感谢点赞支持下哈 

本文分享自微信公众号 - java1234(gh_27ed55ecb177)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索