资讯专栏INFORMATION COLUMN

一文读懂Java线程状态转换

summerpxy / 506人阅读

摘要:前言本文描述线程线程状态及状态转换,不会涉及过多理论,主要以代码示例说明线程状态如何转换。被终止线程执行完毕正常结束或执行过程中因未捕获异常意外终止都会是线程进入被终止状态。线程执行完毕打印状态。

前言

本文描述Java线程线程状态及状态转换,不会涉及过多理论,主要以代码示例说明线程状态如何转换。

基础知识 1. 线程状态

线程可以有6种状态:

New(新建)

Runnable(可运行)

Blocked(被阻塞)

Waiting(等待)

Timed waiting(计时等待)

Terminated(被终止)

New:new Thread()后线程的状态就是新建。

Runnable:线程一旦调用start()方法,无论是否运行,状态都为Runable,注意Runable状态指示表示线程可以运行,不表示线程当下一定在运行,线程是否运行由虚拟机所在操作系统调度决定。

被阻塞:线程试图获取一个内部对象的Monitor(进入synchronized方法或synchronized块)但是其他线程已经抢先获取,那此线程被阻塞,知道其他线程释放Monitor并且线程调度器允许当前线程获取到Monitor,此线程就恢复到可运行状态。

等待:当一个线程等待另一个线程通知调度器一个条件时,线程进入等待状态。

计时等待:和等待类似,某些造成等待的方法会允许传入超时参数,这类方法会造成计时等待,收到其他线程的通知或者超时都会恢复到可运行状态。

被终止:线程执行完毕正常结束或执行过程中因未捕获异常意外终止都会是线程进入被终止状态。

2. 线程状态转换

线程从“新建”到“被终止”会历经多次状态转换,所有可能的转换如下图:

【图一】

观察状态转化图,我们发现“可运行”状态为所有状态的必经状态。我们分析出四条基本的状态转换线路图。

新建--->可运行--->被终止

新建--->可运行--->被阻塞--->可运行--->被终止

新建--->可运行--->等待--->可运行--->被终止

新建--->可运行--->计时等待--->可运行--->被终止

“新建”和“被终止”状态分别为起始和结束状态,和“可运行”状态不可逆。其他状态均能和“可运行”状态相互转换。

用代码说话

让我们用代码演示线程状态是如何转换的,大家重点关注两个问题?

什么操作会改变线程状态?

改变的状态是如何恢复的?

一、 新建--->可运行--->被终止

这个状态转换时创建的线程生命周期。

/**
 * NEW->RUNNABLE->TERMINATED
 */
public class ThreadStateNRT {

    public static void main(String[] args) {
        Thread thread=new Thread(new Task());
        print(thread.getName(),thread.getState());
        thread.start();
        //等待线程执行完毕。
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        print(thread.getName(),thread.getState());
    }

    private static class Task implements Runnable{
        @Override
        public void run() {
            print(Thread.currentThread().getName(),Thread.currentThread().getState());
        }
    }
    private static final String stringFormat="%s:%s";
    private static void print(String threadName,Thread.State state){
        System.out.println(String.format(stringFormat,threadName,state));
    }
}

其中,print()方法用来打印线程信息。后面的代码示例均不在展示。

运行程序结果为:

Thread-0:NEW 
Thread-0:RUNNABLE
Thread-0:TERMINATED
二、 新建--->可运行--->被阻塞--->可运行--->被终止

只有一种方法能出现阻塞状态,那就是synchronized同步原语。我们需要两个线程其中一个线程被另一个阻塞来展示。

/**
 * NEW->RUNNABLE->BLOCKED->RUNNABLE->TERMINATED
 */
public class ThreadStateNRBRT {
    //锁
    private static final Object lock=new Object();

    public static void main(String[] args) {
        //辅助线程,制造synchronized状态。
        Thread assistantThread = new Thread(new SynTask());
        assistantThread.start();
        try {
            //保证assistantThread先执行。
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Thread showThread = new Thread(new Task());
        print(showThread.getName(), showThread.getState());
        showThread.start();
        print(showThread.getName(),showThread.getState());
        //因为无法判断显示线程何时执行,所以循环直到显示线程执行。
        while (true){
            if(showThread.getState()==Thread.State.BLOCKED){
                print(showThread.getName(), Thread.State.BLOCKED);
                break;
            }
        }
        //等待两个线程执行完毕。
        try {
            assistantThread.join();
            showThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //线程执行完毕打印状态。
        print(showThread.getName(), showThread.getState());
    }

    private static class SynTask implements Runnable {
        @Override
        public void run() {
            //锁定一定时间
            synchronized (lock){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static class Task implements Runnable {
        @Override
        public void run() {
            synchronized (lock){
                print(Thread.currentThread().getName(),Thread.currentThread().getState());
            }
        }
    }
}

执行一下你有可能看到正确结果:

Thread-1:NEW
Thread-1:RUNNABLE
Thread-1:BLOCKED
Thread-1:RUNNABLE
Thread-1:TERMINATED

为什么是有可能呢?我们调整一下代码,例如将加锁的时间调小一点:

private static class SynTask implements Runnable {
        @Override
        public void run() {
            //锁定一定时间
            synchronized (lock){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
}

注意此处Thread.sleep(10),我们只锁住十毫秒。

再运行一下,控制台可能打印出这样的结果且程序不会结束:

Thread-1:NEW
Thread-1:RUNNABLE
Thread-1:RUNNABLE

造成以上结果的原因是我么无法保证两个线程的执行顺序,也无法证主线程一定能打印出显示线程阻塞的状态。

         while (true){
            if(showThread.getState()==Thread.State.BLOCKED){
                print(showThread.getName(), Thread.State.BLOCKED);
                break;
            }
        }

所以执行在这段代码死循环了。

调整一下代码,保证不会因为参数调整改变线程之间的执行顺序和打印结果。

/**
 * NEW->RUNNABLE->BLOCKED->RUNNABLE->TERMINATED
 */
public class ThreadStateNRBRT_New {
    //锁
    private static final Object lock=new Object();
    //锁定标志
    private volatile static boolean lockFlag=true;
    //执行顺序
    private volatile static int order=0;

    public static void main(String[] args) {
        //展示线程
        Thread showThread = new Thread(new Task());
        print(showThread.getName(), showThread.getState());
        showThread.start();
        print(showThread.getName(), showThread.getState());
        //辅助线程,制造synchronized状态。
        Thread assistantThread = new Thread(new SynTask());
        assistantThread.start();


        //循环读取展示线程状态,直到读到展示线程状态为BLOCKED,才让辅助线程退出同步。
        while (true){
            if(showThread.getState()==Thread.State.BLOCKED){
                print(showThread.getName(), Thread.State.BLOCKED);
                lockFlag=false;
                break;
            }
        }

        try {
            assistantThread.join();
            showThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //线程执行完毕打印状态。
        print(showThread.getName(), showThread.getState());
    }

    private static class SynTask implements Runnable {
        @Override
        public void run() {
            while (true) {
                //保证先进入同步范围。
                if (order == 0) {
                    synchronized (lock) {
                        //启动另一个同步
                        order=1;
                        //等待主线程读取到线程阻塞状态,退出同步。
                        while (lockFlag) {
                            try {
                                Thread.sleep(10);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    break;
                }
            }
        }
    }

    private static class Task implements Runnable {
        @Override
        public void run() {
            while (true){
                //保证后进入同步范围。
                if (order==1){
                    synchronized (lock){
                        print(Thread.currentThread().getName(),Thread.currentThread().getState());
                    }
                    break;
                }
            }
        }
    }
}

我们用order保证线程进入同步区的顺序,用lockFlag保证只有在打印出显示线程的被阻塞状态后辅助线程才退出同步区。这样无论如何执行我们都会得到同样的结果。

Thread-0:NEW
Thread-0:RUNNABLE
Thread-0:BLOCKED
Thread-0:RUNNABLE
Thread-0:TERMINATED
三、 新建--->可运行--->等待--->可运行--->被终止

这里我们展示两种三种方法造成线程的等待状态

Object.wait()

java.util.concurrent.locks.Locke.lock()

java.util.concurrent.locks.Condition.await()

其他方法如Thread.join()等大家可以参考示例代码自己实现。

1. Object.wait()
/**
 * NEW->RUNNABLE->WAITING->RUNNABLE->TERMINATED
 */
public class ThreadStateNRWRT {
    //锁
    private static final Object lock=new Object();

    public static void main(String[] args) {
        //展示线程
        Thread showThread = new Thread(new WaitTask());
        print(showThread.getName(), showThread.getState());
        showThread.start();
        print(showThread.getName(),showThread.getState());
        //循环读取展示线程状态,直到读到展示线程状态为WAITING,才让辅助线程唤醒等待线程。
        while (true){
            if(showThread.getState()==Thread.State.WAITING){
                print(showThread.getName(), Thread.State.WAITING);
                break;
            }
        }
        synchronized (lock){
            lock.notify();
        }


        try {
            showThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //线程执行完毕打印状态。
        print(showThread.getName(), showThread.getState());
    }


    private static class WaitTask implements Runnable {
        @Override
        public void run() {
            //等待
            synchronized (lock){
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            print(Thread.currentThread().getName(),Thread.currentThread().getState());
        }
    }
}
2. java.util.concurrent.locks.Locke.lock()
/**
 * NEW->RUNNABLE->WAITING->RUNNABLE->TERMINATED
 */
public class ThreadStateNRWRTLock {
    //锁
    private  static Lock lock=new ReentrantLock();
    //锁定标志
    private volatile static boolean lockFlag=true;
    //执行顺序
    private volatile static int order=0;

    public static void main(String[] args) {
        //展示线程
        Thread showThread = new Thread(new Task());
        print(showThread.getName(), showThread.getState());
        showThread.start();
        print(showThread.getName(), showThread.getState());
        //辅助线程,制造synchronized状态。
        Thread assistantThread = new Thread(new SynTask());
        assistantThread.start();
        //循环读取展示线程状态,直到读到展示线程状态为BLOCKED,才让辅助线程退出同步。
        while (true){
            if(showThread.getState()==Thread.State.WAITING){
                print(showThread.getName(), Thread.State.WAITING);
                lockFlag=false;
                break;
            }
        }
        try {
            assistantThread.join();
            showThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //线程执行完毕打印状态。
        print(showThread.getName(), showThread.getState());
    }

    private static class SynTask implements Runnable {
        @Override
        public void run() {
            while (true) {
                //保证先进入同步范围。
                if (order == 0) {
                    //加锁
                    lock.lock();
                    try {
                        order=1;
                        while (lockFlag) {
                            try {
                                Thread.sleep(10);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }finally {
                        lock.unlock();
                    }
                    break;
                }
            }
        }
    }

    private static class Task implements Runnable {
        @Override
        public void run() {
            while (true){
                //保证后进入同步范围。
                if (order==1){
                    lock.lock();
                    try{
                        print(Thread.currentThread().getName(),Thread.currentThread().getState());
                    }finally {
                        lock.unlock();
                    }
                    break;
                }
            }
        }
    }
}
3. java.util.concurrent.locks.Condition.await()
/**
 * NEW->RUNNABLE->WAITING->RUNNABLE->TERMINATED
 */
public class ThreadStateNRWRTCondition {
    //锁
    private static Lock lock=new ReentrantLock();
    private static Condition condition=lock.newCondition();

    public static void main(String[] args) {
        //展示线程
        Thread showThread = new Thread(new WaitTask());
        print(showThread.getName(), showThread.getState());
        showThread.start();
        print(showThread.getName(),showThread.getState());
        //循环读取展示线程状态,直到读到展示线程状态为WAITING,才让辅助线程唤醒等待线程。
        while (true){
            if(showThread.getState()==Thread.State.WAITING){
                print(showThread.getName(), Thread.State.WAITING);
                break;
            }
        }

        lock.lock();
        try{
            condition.signal();
        }finally {
            lock.unlock();
        }


        try {
            showThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //线程执行完毕打印状态。
        print(showThread.getName(), showThread.getState());
    }


    private static class WaitTask implements Runnable {
        @Override
        public void run() {
            //等待
            lock.lock();
            try{
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }finally {
                lock.unlock();
            }
            print(Thread.currentThread().getName(),Thread.currentThread().getState());
        }
    }
}
4. 运行结果

三段代码的运行结果都是:

Thread-0:NEW
Thread-0:RUNNABLE
Thread-0:WAITING
Thread-0:RUNNABLE
Thread-0:TERMINATED
四、新建--->可运行--->计时等待--->可运行--->被终止

我们展示两个方法造成计时等待状态

Object.wait(long timeout)

java.util.concurrent.locks.Condition.await(long time, TimeUnit unit)

其他方法如Thread.sleep(long millis),Thread.join(long millis)等大家可以自己实现。
感觉凡是有超时方法的方法都能让线程状态进入计时等待,但是这个没有经过验证,所以只是一个猜想。

1. Object.wait(long timeout)
/**
 * NEW->RUNNABLE->TIMED_WAITING->RUNNABLE->TERMINATED
 */
public class ThreadStateNRTWRT {
    //锁
    private static final Object lock=new Object();

    public static void main(String[] args) {
        //展示线程
        Thread showThread = new Thread(new WaitTask());
        print(showThread.getName(), showThread.getState());
        showThread.start();
        print(showThread.getName(),showThread.getState());
        //循环读取展示线程状态,直到读到展示线程状态为TIMED_WAITING。
        while (true){
            if(showThread.getState()==Thread.State.TIMED_WAITING){
                print(showThread.getName(), Thread.State.TIMED_WAITING);
                break;
            }
        }

        try {
            showThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //线程执行完毕打印状态。
        print(showThread.getName(), showThread.getState());
    }


    private static class WaitTask implements Runnable {
        @Override
        public void run() {
            //等待
            synchronized (lock){
                try {
                    lock.wait(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            print(Thread.currentThread().getName(),Thread.currentThread().getState());
        }
    }
}
2. java.util.concurrent.locks.Condition.await(long time, TimeUnit unit)
/**
 * NEW->RUNNABLE->TIMED_WAITING->RUNNABLE->TERMINATED
 */
public class ThreadStateNRTWRTCondition {
    //锁
    private static Lock lock=new ReentrantLock();
    private static Condition condition=lock.newCondition();

    public static void main(String[] args) {
        //展示线程
        Thread showThread = new Thread(new WaitTask());
        print(showThread.getName(), showThread.getState());
        showThread.start();
        print(showThread.getName(),showThread.getState());
        //循环读取展示线程状态,直到读到展示线程状态为TIMED_WAITING。
        while (true){
            if(Thread.State.TIMED_WAITING==showThread.getState()){
                print(showThread.getName(), Thread.State.TIMED_WAITING);
                break;
            }
        }
        try {
            showThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //线程执行完毕打印状态。
        print(showThread.getName(), showThread.getState());
    }


    private static class WaitTask implements Runnable {
        @Override
        public void run() {
            //等待
            lock.lock();
           try{
                try {
                    condition.await(1,TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }finally {
               lock.unlock();
           }
            print(Thread.currentThread().getName(),Thread.currentThread().getState());
        }
    }
}
3. 运行结果

两段程序的运行结果相同:

Thread-0:NEW
Thread-0:RUNNABLE
Thread-0:TIMED_WAITING
Thread-0:RUNNABLE
Thread-0:TERMINATED
结语

至此,我们已经介绍了线程状态转换的所有情况,了解线程状态转换对分析多线程代码运行很帮助。希望本篇文章对大家今后工作有所助力。

参考

《Java核心技术+卷1》第九版

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/76926.html

相关文章

  • 一文读懂 Java 中的原子类

    摘要:一无锁方案并发包中的原子类都是基于无锁方案实现的,相较于传统的互斥锁,无锁并没有加锁解锁线程切换的消耗,因此无锁解决方案的性能更好,同时无锁还能够保证线程安全。线程首先读取的值并加,如果此时有另一个线程更新了,则期望值和不相等,更新失败。 一、无锁方案 Java 并发包中的原子类都是基于无锁方案实现的,相较于传统的互斥锁,无锁并没有加锁、解锁、线程切换的消耗,因此无锁解决方案的性能更好...

    jas0n 评论0 收藏0
  • JVM(2)--一文读懂垃圾回收

    摘要:对象存不进去,会又一次触发垃圾回收。也就是说,它在进行垃圾回收时,必须暂停其他所有线程。我们来看一个名词吞吐量。吞吐量运行用户代码时间运行用户代码时间垃圾收集时间。也就是说,收集器会严格控制吞吐量,至于这个吞吐量是多少,这个可以人为设置。 与其他语言相比,例如c/c++,我们都知道,java虚拟机对于程序中产生的垃圾,虚拟机是会自动帮我们进行清除管理的,而像c/c++这些语言平台则需要...

    MRZYD 评论0 收藏0
  • 一文读懂贝叶斯推理问题:MCMC方法和变分推断

    摘要:本文将讨论两种可用于解决贝叶斯推理问题的主要方法基于采样的马尔可夫链蒙特卡罗,简称方法和基于近似的变分推理,简称方法。而贝叶斯推理则是从贝叶斯的角度产生统计推断的过程。贝叶斯推理问题还可能会产生一些其他的计算困难。 全文共6415字,预计学习时长20分钟或更长 showImg(https://segmentfault.com/img/bVbvFZZ?w=1280&h=853); 图片来...

    msup 评论0 收藏0

发表评论

0条评论

summerpxy

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<