【Java併發學習】之詳解線程的點滴(1)

【Java併發學習】之詳解線程的點滴(1)

前言

在前面的幾個小節中,我們粗略地學習了線程的基本概念,定義任務的方式,並且通過Java中的多線程API來驅動對應的任務,然後學習了線程的互斥與同步關係,線程之間進行資源共享後的同步操作等等一些簡單的內容,可以說,線程中的主要內容我們已經掌握了,然而,也僅僅只是簡單內容,很多比較細節、複雜的東西在之前我們刻意避開了,接下來的幾個小節中,我們就來具體地學習線程的各個細節內容

線程的屬性

線程有許多的屬性,比如線程的ID、名字、優先級、狀態等,接下來我們就來具體學習這些屬性

線程的ID

每個線程都有對應的獨一無二的ID,這些ID是由JVM在運行的時候爲線程設置的,用於區分不同的線程,並且不對外提供設置線程ID的接口,也就是說,在Java中,我們僅能夠獲取多線程的ID,而不能設置線程的ID,訪問代碼如下


    class DetailTask implements Runnable{

        @Override
        public void run() {
            System.out.println("Details : " + Thread.currentThread().getId());
            // 其中Thread.currentThread()用於獲取當前執行的線程
            // 通過thread.getId()則可以獲取對應的線程的ID
        }
    }

線程的名字

在線程比較多的情況下,我們會傾向於根據執行不同任務的不同線程來爲其設置名字,方便在日誌以及其他場景中使用到,設置以及獲取線程的名字操作具體如下


    class DetailTask implements Runnable{

        private static int cnt = 0;
        // 注意這裏的代碼是非線程安全的
        @Override
        public void run() {
            Thread.currentThread().setName("Thread " + cnt++); // 設置線程名字
            System.out.println("Details : " + Thread.currentThread().getName()); // 獲取線程的名字
        }
    }

線程優先級

優先級是一個比較有價值的概念,在Java中,線程之間的調度採用的是基於時間片的輪轉調度方式,其中在線程進行切換,重新調度時,調度的主要依據就是優先級,通常來說,優先級比較高的線程,被調度的可能性會比較大,也就是說,在單位時間內,優先級高的線程被調度的次數會大於優先級低的線程

不過,在設置線程的優先級的時候,需要注意一下幾點

  • 線程的有效優先級僅能爲 0 - 10 之間的整數,其他數字則會拋出java.lang.IllegalArgumentException
  • 由於不同的操作系統的優先級的範圍不一致,而映射到0-10之間則會出現一些意想不到的驚喜,所以一般在設置優先級的時候,不能過分依賴於優先級,儘量只使用Thread.MAX_PRIORITYThread.MIN_PRIORITY以及默認的優先級,分別對應的數值爲10,0,1
  • 設置優先級僅僅僅能提高線程被調度的概率,也就是僅能提高線程被調度的次數,但是並不能保證執行次數一定會高,所以,不能僅依賴優先級來實現如同步、合作之類的操作
  • 優先級的設置必須在線程啓動之前設置,也就是必須在thread.start()方法執行之前進行設置,否則,會採用默認的優先級,言下之意即不能在run方法中進行優先級的設置

    for (int i = 0; i < 10; i++){
        Thread thread = new Thread(task);
        System.out.println("Priority " + thread.getPriority());
        if (i % 2 == 0){
            thread.setPriority(Thread.MIN_PRIORITY); // 設置優先級爲最低
        }else {
            thread.setPriority(Thread.MAX_PRIORITY); // 設置優先級爲最高
        }
        thread.start();
    }

線程的狀態

通常來講,線程的數量是多於CPU的數量,這也就意味着,爲了保證每個線程都能得到執行,在特定時刻,總會有線程處於非運行狀態,當然,Java中線程的狀態不止這兩種,具體如下

  • NEW:新建狀態,此時線程已經被賦予了任務,但需要注意的是,此時執行該線程所需要的資源仍未準備好,是不具備執行條件的,也就是在調用start()方法之前的狀態
  • RUNNABLE:可運行狀態,表明此時該線程已經是處於可執行的狀態,這裏需要注意的是,可運行狀態可以是該線程正在執行,也可能該線程的除了CPU之外的其他資源都已經準備完畢,只要分配給其CPU就能執行。注意是Runnable而不是Run
  • BLOCKED:阻塞狀態,線程如果處於該狀態,則表明此時該線程正在等在某個鎖的釋放
  • WAITING:等待狀態,表明此時該線程正在等待某些其他線程的資源,通常是調用了Object.wait()join()LockSupport.park()這幾個方法
  • TIMED_WAITING:等待超時狀態,同樣是等待狀態,只是多了個計時器,通常是調用了上面幾個方法中帶時間參數的重載方法
  • TERMINATED:中斷/結束狀態,表明此時該線程已經處於執行完畢的狀態或者是被其他線程或者JVM中斷,結束其運行

關於這幾個狀態的詳細介紹,可以參考JDK源碼中的註釋


    /* A thread can be in only one state at a given point in time.
     * These states are virtual machine states which do not reflect
     * any operating system thread states.
     *
     * @since   1.5
     * @see #getState
     */
    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;
    }

守護線程

上面我們主要學習了線程的幾個屬性,ID、名字、優先級、狀態等,接下來我們來學習一個新的概念,守護線程

守護線程是一類特殊的線程,主要的特點如下

  • 被調度的頻率較低
  • 噹噹前系統中的所有非守護線程結束時,守護線程也會結束,而且不管正在執行什麼任務

基於守護線程的特點,在使用的時候需要注意,不能在守護線程中做一些邏輯比較複雜的操作,不能做一些比較重要的操作

一個典型的使用守護線程的例子就是JVM中的垃圾回收器


    public static void main(String[] args) {
        Thread daemonTask = new Thread(new DaemonTask());
        daemonTask.setDaemon(true);// 設置爲守護線程,這裏同樣需要注意,必須在線程啓動之前進行設置
        daemonTask.start();
    }

    class DaemonTask implements Runnable{
        @Override
        public void run() {
            while (true){
                System.out.println("I'm Daemon thread");
            }
        }
    }

總結

本小計我們主要學習了線程的相關屬性,包括了ID、名字、優先級、狀態,以及守護線程的概念和設置守護線程的方法,線程中的異常處理,接下來我們將學習線程的中斷以及線程的異常處理等等的內容

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章