併發編程之線程第一篇

3.4 原理之線程運行

Java虛擬機棧
JVM中由堆、棧、方法區所組成,其中棧內存是給線程使用,每一個線程啓動後,虛擬機就會爲其分配一塊棧內存。java

  • 每一個棧由多個棧幀(Frame)組成,對應着每次方法調用時所佔用的內存
  • 每一個線程只能有一個活動棧幀,對應着當前正在執行的那個方法

線程上下文切換(Thread Context Switch)

由於如下一些緣由致使cpu再也不執行當前的線程,轉而執行另外一個線程的代碼編程

  • 線程的cpu時間片用完
  • 垃圾回收
  • 有更高優先級的線程須要運行
  • 線程本身調用了sleep、yield、join、park、synchronized、lock等方法程序
    當Context Switch發生時,須要由操做系統保存當前線程的狀態,並恢復另外一個線程的狀態,Java中對應的概念就是程序計數器(Program Counter Register),它的做用是記住下一條jvm指令的執行地址,是線程私有的
    在這裏插入圖片描述

3.5 常見方法

方法名 功能說明 注意
start() 啓動一個新線程,在新的線程運行run方法中的代碼 start方法只是讓線程進入就緒,裏面的代碼不必定馬上運行(CPU的時間片還沒分給它)。每一個線程對象的start方法只能調用一次,若是調用了屢次會出現IllegalThreadStateException
run() 新線程啓動後會調用的方法 若是在構造Thread對象時傳遞了Runnable參數,則線程啓動後調用Runnable中的run 方法,不然默認不執行任何操做。但能夠建立Thread的子類對象,來覆蓋默認行爲
join() 等待線程運行結束  
join(long n) 等待線程運行結束,最多等待n毫秒  
getId() 獲取線程長整型的id id惟一
getName() 獲取線程名  
setName(String) 修改線程名  
getPriority() 獲取線程優先級  
setPriority(int) 修改線程優先級 java中規定線程優先級是1~10的整數,較大的優先級能提升該線程被CPU調度的機率
getState() 獲取線程狀態 java中線程狀態是用6個enum表示,分別爲 :NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED
isInterrupted() 斷定是否被打斷 不會清除打斷標記
isAlive() 線程是否存活(尚未運行完畢)  
interrupt() 打斷線程 若是被打斷線程正在sleep,wait,join會致使被打斷的線程拋出InterruptedException,並清除打斷標記;若是打斷的正在運行的線程,則會設置打斷標記;park的線程被打斷,也會設置打斷標記
interrupted() static 斷定當前線程是否被打斷 會清除打斷標記
currendThread(0 static 獲取當前正在執行的線程  
sleep(long n) static 讓當前執行的線程休眠n毫秒,休眠時讓出cpu的時間片給其餘線程  
yieId() static 提示線程調度器讓出當前線程對CPU的使用 主要是爲了測試和調試

3.6 start與run

調用run
在這裏插入圖片描述
輸出
在這裏插入圖片描述
程序仍在main線程運行,FileReader.read()方法調用仍是同步的併發

3.7 sleep與yield

sleepapp

  1. 調用sleep會讓當前線程從Running進入Timed Waiting狀態
  2. 其它線程可使用 interrupt方法打斷正在睡眠的線程,這時sleep方法會拋出InterruptedException
  3. 睡眠結束後的線程未必會馬上獲得執行
  4. 建議用TimeUnit的sleep代替Thread的sleep來得到更好地可讀性
    yield
    一、 調用yield會讓當前線程從Running進入Runnable狀態,而後調度執行其它同優先級的線程。若是這時沒有同優先級的線程,那麼不能保證讓當前線程暫停的效果
    二、具體的實現依賴於操做系統的任務調度器
    線程優先級
  • 線程優先級會提示(hint)調度器優先調度該線程,但它僅僅是一個提示,調度器能夠忽略它
  • 若是cpu比較忙,那麼優先級高的線程會得到更多的時間片,但cpu閒時,優先級幾乎沒做用
    在這裏插入圖片描述

案例 - 防止CPU佔用100%

sleep實現
在沒有利用cpu來計算時,不要讓while(true)空轉浪費cpu,這時可使用yield或sleep來讓出cpu的使用權給其餘程序
在這裏插入圖片描述異步

  • 能夠用wait或條件變量達到相似的效果
  • 不一樣的是,後兩種都須要加鎖,而且須要相應的喚醒操做,通常適用於要進行同步的場景
  • sleep適用於無需鎖同步的場景

3.8 join方法詳解

爲何須要join
下面的代碼執行,打印r是什麼?
在這裏插入圖片描述
分析jvm

  • 由於主線程和線程t1是並行執行的,t1線程須要1秒以後才能算出r=10
  • 而主線程一開始就要打印r的結果,因此只能打印出r=0
    解決方法
  • 用sleep行不行?爲何?
  • 用join,加在start以後便可
    在這裏插入圖片描述
    應用之同步 (案例1)
    以調用方角度來說,若是
  • 須要等待結果返回,才能繼續運行就是同步
  • 不須要等待結果返回,就能繼續運行就是異步
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    有時效的join
    等夠時間
    在這裏插入圖片描述
    輸出
    在這裏插入圖片描述

3.9 interrupt方法詳解

打斷sleep,wait,join的線程
打斷sleep的線程,會清空打斷狀態,以sleep爲例
在這裏插入圖片描述
輸出
在這裏插入圖片描述
打斷正常運行的線程
打斷正常運行的線程,不會清空打斷狀態
在這裏插入圖片描述
輸出
在這裏插入圖片描述測試

兩階段終止模式

Two Phase Termination
在一個線程T1中如何「優雅」終止線程T2?這裏的【優雅】指的是給T2一個料理後事的機會。
一、 錯誤思路操作系統

  • 使用線程對象的stop()中止線程
    (1)stop方法會真正殺死線程,若是這時線程鎖住了共享資源,那麼當它被殺死後就再也沒有機會釋放鎖,其它線程將永遠沒法獲取鎖。
  • 使用System.exit(int)方法中止線程
    (1)目的僅是中止一個線程,但這種作法會讓整個程序都中止
    在這裏插入圖片描述
package com.example.demo;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class TwoPhaseTermination {
    public static void main(String[] args) throws InterruptedException {
        Test test = new Test();
        test.start();

        Thread.sleep(3500);
        test.stop();
    }
}

@Slf4j
class Test{
    private Thread monitor;

    /**
     * 啓動監控線程
     */
    public void start() {
        monitor = new Thread(() -> {
            while (true) {
                Thread currentThread = Thread.currentThread();
                if (currentThread.isInterrupted()) {
                    log.info("料理後事");
                    break;
                }
                try {
                    // 狀況1
                    Thread.sleep(1000);
                    log.info("執行監控記錄");
                } catch (Exception e) {
                	// 由於sleep出現異常後,會消除打斷標記
                    // 須要重置打斷標記
                    e.printStackTrace();
                    currentThread.interrupt();
                }
            }
        });
        monitor.start();
    }

    /**
     * 中止監控線程
     */
    public void stop() {
        monitor.interrupt();
    }

}

在這裏插入圖片描述
打斷park線程
打斷park線程,不會清空打斷狀態
在這裏插入圖片描述
輸出
在這裏插入圖片描述
若是打斷標記已是true,則park會失效,能夠以下操做 :
在這裏插入圖片描述.net

3.10 不推薦的方法

還有一些不推薦使用的方法,這些方法已過期,容易破壞同步代碼塊,形成線程死鎖。線程

方法名 static 功能說明
stop()   中止線程運行
suspend()   掛起(暫停)線程運行
resume()   恢復線程運行

3.11 主線程與守護線程

默認狀況下,Java進程須要等待全部線程都運行結束,纔會結束。有一種特殊的線程叫作守護線程,只要其它非守護線程運行結束了,即便守護線程的代碼沒有執行完,也會強制結束。
在這裏插入圖片描述
在這裏插入圖片描述
注意

  • 垃圾回收器線程就是一種守護線程
  • Tomcat中的Acceptor和Poller線程都是守護線程,因此Tomcat接收到shutdown命令後,不會等待它們處理完當前請求。
相關文章
相關標籤/搜索