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源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索