一文讀懂Java線程狀態轉換

前言

本文描述Java線程線程狀態及狀態轉換,不會涉及過多理論,主要以代碼示例說明線程狀態如何轉換。java

基礎知識

1. 線程狀態

線程能夠有6種狀態:多線程

  • New(新建)
  • Runnable(可運行)
  • Blocked(被阻塞)
  • Waiting(等待)
  • Timed waiting(計時等待)
  • Terminated(被終止)
  1. New:new Thread()後線程的狀態就是新建。
  2. Runnable:線程一旦調用start()方法,不管是否運行,狀態都爲Runable,注意Runable狀態指示表示線程能夠運行,不表示線程當下必定在運行,線程是否運行由虛擬機所在操做系統調度決定。
  3. 被阻塞:線程試圖獲取一個內部對象的Monitor(進入synchronized方法或synchronized塊)可是其餘線程已經搶先獲取,那此線程被阻塞,知道其餘線程釋放Monitor而且線程調度器容許當前線程獲取到Monitor,此線程就恢復到可運行狀態。
  4. 等待:當一個線程等待另外一個線程通知調度器一個條件時,線程進入等待狀態。
  5. 計時等待:和等待相似,某些形成等待的方法會容許傳入超時參數,這類方法會形成計時等待,收到其餘線程的通知或者超時都會恢復到可運行狀態。
  6. 被終止:線程執行完畢正常結束或執行過程當中因未捕獲異常意外終止都會是線程進入被終止狀態。

2. 線程狀態轉換

線程從「新建」到「被終止」會歷經屢次狀態轉換,全部可能的轉換以下圖:ide

clipboard.png

【圖一】spa

觀察狀態轉化圖,咱們發現「可運行」狀態爲全部狀態的必經狀態。咱們分析出四條基本的狀態轉換線路圖。操作系統

  • 新建--->可運行--->被終止
  • 新建--->可運行--->被阻塞--->可運行--->被終止
  • 新建--->可運行--->等待--->可運行--->被終止
  • 新建--->可運行--->計時等待--->可運行--->被終止

「新建」和「被終止」狀態分別爲起始和結束狀態,和「可運行」狀態不可逆。其餘狀態均能和「可運行」狀態相互轉換。線程

用代碼說話

讓咱們用代碼演示線程狀態是如何轉換的,你們重點關注兩個問題?code

  • 什麼操做會改變線程狀態?
  • 改變的狀態是如何恢復的?

1、 新建--->可運行--->被終止

這個狀態轉換時建立的線程生命週期。orm

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

2、 新建--->可運行--->被阻塞--->可運行--->被終止

只有一種方法能出現阻塞狀態,那就是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

3、 新建--->可運行--->等待--->可運行--->被終止

這裏咱們展現兩種三種方法形成線程的等待狀態

  • 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

4、新建--->可運行--->計時等待--->可運行--->被終止

咱們展現兩個方法形成計時等待狀態

  • 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》第九版

相關文章
相關標籤/搜索