<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                ## 27 Thread 源碼解析 ## 引導語 從本章開始我們開始學習線程的知識,線程是非常有趣的一個章節,大多數同學對于線程 API,屬于不用就忘,到用時需要百度的情況,希望通過本小節的源碼閱讀,能夠加深對線程的印象。 本小節主要三章,本章主要說線程的基本概念、使用姿勢、Thread 和 Runnable 的源碼;Future、ExecutorService 源碼解析章節主要說異步線程執行;押寶線程源碼面試題章節主要說說常遇到的源碼面試題。 由于線程的概念很多,所以本章會先介紹很多線程的基本概念,說清楚后再解析源碼,不然有些同學會看不懂,大家見諒。 ### 1 類注釋 #### 1.1 Thread 1. 每個線程都有優先級,高優先級的線程可能會優先執行; 2. 父線程創建子線程后,優先級、是否是守護線程等屬性父子線程是一致的; 3. JVM 啟動時,通常都啟動 MAIN 非守護線程,以下任意一個情況發生時,線程就會停止: 退出方法被調用,并且安全機制允許這么做(比如調用 Thread.interrupt 方法); 所有非守護線程都消亡,或者從運行的方法正常返回,或者運行的方法拋出了異常; 4. 每個線程都有名字,多個線程可能具有相同的名字,Thread 有的構造器如果沒有指定名字,會自動生成一個名字。 ### 2 線程的基本概念 我們接下來介紹一下線程的基本概念: #### 2.1 線程的狀態 網上有各種介紹線程狀態的文章,我們這里說線程的狀態是從源碼的角度,源碼中一共列舉了六種狀態,如下圖: 我們解析一下這個圖: ![](https://img.kancloud.cn/72/1d/721d799c6ca25da9b9ea55d8f0080ba1_1249x548.jpg) 1. NEW 表示線程創建成功,但沒有運行,在 new Thread 之后,沒有 start 之前,線程的狀態都是 NEW; 2. 當我們運行 strat 方法,子線程被創建成功之后,子線程的狀態變成 RUNNABLE,RUNNABLE 表示線程正在運行中; 3. 子線程運行完成、被打斷、被中止,狀態都會從 RUNNABLE 變成 TERMINATED,TERMINATED 表示線程已經運行結束了; 4. 如果線程正好在等待獲得 monitor lock 鎖,比如在等待進入 synchronized 修飾的代碼塊或方法時,會從 RUNNABLE 變成 BLOCKED,BLOCKED 表示阻塞的意思; 5. WAITING 和 TIMEDWAITING 類似,都表示在遇到 Object#wait、Thread#join、LockSupport#park 這些方法時,線程就會等待另一個線程執行完特定的動作之后,才能結束等待,只不過 TIMEDWAITING 是帶有等待時間的(可以看下面的 join 方法的 demo)。 再次重申,這 6 種狀態并不是線程所有的狀態,只是在 Java 源碼中列舉出的 6 種狀態, Java 線程的處理方法都是圍繞這 6 種狀態的。 #### 2.2 優先級 優先級代表線程執行的機會的大小,優先級高的可能先執行,低的可能后執行,在 Java 源碼中,優先級從低到高分別是 1 到 10,線程默認 new 出來的優先級都是 5,源碼如下: ``` // 最低優先級 public final static int MIN_PRIORITY = 1; // 普通優先級,也是默認的 public final static int NORM_PRIORITY = 5; // 最大優先級 public final static int MAX_PRIORITY = 10; ``` #### 2.3 守護線程 我們默認創建的線程都是非守護線程。創建守護線程時,需要將 Thread 的 daemon 屬性設置成 true,守護線程的優先級很低,當 JVM 退出時,是不關心有無守護線程的,即使還有很多守護線程,JVM 仍然會退出,我們在工作中,可能會寫一些工具做一些監控的工作,這時我們都是用守護子線程去做,這樣即使監控拋出異常,但因為是子線程,所以也不會影響到業務主線程,因為 是守護線程,所以 JVM 也無需關注監控是否正在運行,該退出就退出,所以對業務不會產生任何影響。 #### 2.4 ClassLoader ClassLoader 我們可以簡單理解成類加載器,就是把類從文件、二進制數組、URL 等位置加載成可運行 Class。 ### 3 線程兩種初始化方式 無返回值的線程主要有兩種初始化方式: #### 3.1 繼承 Thread,成為 Thread 的子類 ``` // 繼承 Thread,實現其 run 方法 class MyThread extends Thread{ @Override public void run() { log.info(Thread.currentThread().getName()); } } @Test // 調用 start 方法即可,會自動調用到 run 方法的 public void extendThreadInit(){ new MyThread().start(); } ``` 上述代碼打印出的線程名稱是:Thread-0,而主線程的名字是:Thread [main,5,main],由此可見,的確是開了一個子線程來執行打印的操作。 我們一起來看下 start 的底層源碼: ``` // 該方法可以創建一個新的線程出來 public synchronized void start() { // 如果沒有初始化,拋異常 if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); // started 是個標識符,我們在做一些事情的時候,經常這么寫 // 動作發生之前標識符是 false,發生完成之后變成 true boolean started = false; try { // 這里會創建一個新的線程,執行完成之后,新的線程已經在運行了,既 target 的內容已經在運行了 start0(); // 這里執行的還是主線程 started = true; } finally { try { // 如果失敗,把線程從線程組中刪除 if (!started) { group.threadStartFailed(this); } // Throwable 可以捕捉一些 Exception 捕捉不到的異常,比如說子線程拋出的異常 } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } } // 開啟新線程使用的是 native 方法 private native void start0(); ``` #### 3.2 實現 Runnable 接口,作為 Thread 的入參 ``` Thread thread = new Thread(new Runnable() { @Override public void run() { log.info("{} begin run",Thread.currentThread().getName()); } }); // 開一個子線程去執行 thread.start(); // 不會新起線程,是在當前主線程上繼續運行 thread.run(); ``` 這種就是實現 Runnable 的接口,并作為 Thread 構造器的入參,我們調用時使用了兩種方式,可以根據情況選擇使用 start 或 run 方法,使用 start 會開啟子線程來執行 run 里面的內容,使用 run 方法執行的還是主線程。 我們來看下 run 方法的源碼: ``` // 簡單的運行,不會新起線程,target 是 Runnable public void run() { if (target != null) { target.run(); } } ``` 源碼中的 target 就是在 new Thread 時,賦值的 Runnable。 ### 4 線程初始化 線程初始化的源碼有點長,我們只看比較重要的代碼 (不重要的被我刪掉了),如下: ``` // 無參構造器,線程名字自動生成 public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } // g 代表線程組,線程組可以對組內的線程進行批量的操作,比如批量的打斷 interrupt // target 是我們要運行的對象 // name 我們可以自己傳,如果不傳默認是 "Thread-" + nextThreadNum(),nextThreadNum 方法返回的是自增的數字 // stackSize 可以設置堆棧的大小 private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) { if (name == null) { throw new NullPointerException("name cannot be null"); } this.name = name.toCharArray(); // 當前線程作為父線程 Thread parent = currentThread(); this.group = g; // 子線程會繼承父線程的守護屬性 this.daemon = parent.isDaemon(); // 子線程繼承父線程的優先級屬性 this.priority = parent.getPriority(); // classLoader if (security == null || isCCLOverridden(parent.getClass())) this.contextClassLoader = parent.getContextClassLoader(); else this.contextClassLoader = parent.contextClassLoader; this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext(); this.target = target; setPriority(priority); // 當父線程的 inheritableThreadLocals 的屬性值不為空時 // 會把 inheritableThreadLocals 里面的值全部傳遞給子線程 if (parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); this.stackSize = stackSize; /* Set thread ID */ // 線程 id 自增 tid = nextThreadID(); } ``` 從初始化源碼中可以看到,很多屬性,子線程都是直接繼承父線程的,包括優先性、守護線程、inheritableThreadLocals 里面的值等等。 ### 5 線程其他操作 #### 5.1 join join 的意思就是當前線程等待另一個線程執行完成之后,才能繼續操作,我們寫了一個 demo,如下: ``` @Test public void join() throws Exception { Thread main = Thread.currentThread(); log.info("{} is run。",main.getName()); Thread thread = new Thread(new Runnable() { @Override public void run() { log.info("{} begin run",Thread.currentThread().getName()); try { Thread.sleep(30000L); } catch (InterruptedException e) { e.printStackTrace(); } log.info("{} end run",Thread.currentThread().getName()); } }); // 開一個子線程去執行 thread.start(); // 當前主線程等待子線程執行完成之后再執行 thread.join(); log.info("{} is end", Thread.currentThread()); } ``` 執行的結果,就是主線程在執行 thread.join (); 代碼后會停住,會等待子線程沉睡 30 秒后再執行,這里的 join 的作用就是讓主線程等待子線程執行完成,我們畫一個圖示意一下: ![](https://img.kancloud.cn/9d/4c/9d4cdacb85092505a7568e34716d29f0_1149x447.jpg) 從圖中可以看出,主線程一直等待子線程沉睡 30s 后才繼續執行,在等待期間,主線程的狀態也是TIMED_WAITING。 #### 5.2 yield yield 是個 native 方法,底層代碼如下: ``` public static native void yield(); ``` 意思是當前線程做出讓步,放棄當前 cpu,讓 cpu 重新選擇線程,避免線程過度使用 cpu,我們在寫 while 死循環的時候,預計短時間內 while 死循環可以結束的話,可以在循環里面使用 yield 方法,防止 cpu 一直被 while 死循環霸占。 有點需要說明的是,讓步不是絕不執行,重新競爭時,cpu 也有可能重新選中自己。 #### 5.3 sleep sleep 也是 native 方法,可以接受毫秒的一個入參,也可以接受毫秒和納秒的兩個入參,意思是當前線程會沉睡多久,沉睡時不會釋放鎖資源,所以沉睡時,其它線程是無法得到鎖的。 接受毫秒和納秒兩個入參時,如果給定納秒大于等于 0.5 毫秒,算一個毫秒,否則不算。 #### 5.4 interrupt interrupt 中文是打斷的意思,意思是可以打斷中止正在運行的線程,比如: 1. Object#wait ()、Thread#join ()、Thread#sleep (long) 這些方法運行后,線程的狀態是 WAITING 或 TIMED_WAITING,這時候打斷這些線程,就會拋出 InterruptedException 異常,使線程的狀態直接到 TERMINATED; 2. 如果 I/O 操作被阻塞了,我們主動打斷當前線程,連接會被關閉,并拋出 ClosedByInterruptException 異常; 我們舉一個例子來說明如何打斷 WAITING 的線程,代碼如下: ``` @Test public void testInterrupt() throws InterruptedException { Thread thread = new Thread(new Runnable() { @Override public void run() { log.info("{} begin run",Thread.currentThread().getName()); try { log.info("子線程開始沉睡 30 s"); Thread.sleep(30000L); } catch (InterruptedException e) { log.info("子線程被打斷"); e.printStackTrace(); } log.info("{} end run",Thread.currentThread().getName()); } }); // 開一個子線程去執行 thread.start(); Thread.sleep(1000L); log.info("主線程等待 1s 后,發現子線程還沒有運行成功,打斷子線程"); thread.interrupt(); } ``` 例子主要說的是,主線程會等待子線程執行 1s,如果 1s 內子線程還沒有執行完,就會打斷子線程,子線程被打斷后,會拋出 InterruptedException 異常,執行結束,運行的結果如下圖: ![](https://img.kancloud.cn/8d/f5/8df54e2c96279e33016cc660c797743e_1879x505.jpg) ### 6 總結 本章主要介紹了線程的基本概念、狀態、無返回值線程的初始化方式和線程的常用操作,這些知識也是工作中常用的,也是大家都必須了解的,為后面的學習打下基礎。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看