程序員:多併發基礎的線程【詳細版】

本博客 貓叔的博客,轉載請申明出處java

閱讀本文約 「15分鐘」git

適讀人羣:Java 初級github

學習筆記windows

基礎概念

線程是無處不在的緩存

先說說幾個基本的概念吧安全

一個進程中能夠包含多個線程,同一個進程中的線程共享該進程所申請到的資源,如內存空間和文件句柄等bash

從JVM的角度來看,線程是進程中的一個組件(Component)多線程

Java程序中任何一段代碼老是執行在某個肯定的線程中架構

Java中線程分爲守護線程(Daemon Thread)和用戶線程(User Thread)併發

用戶線程:JVM正常中止前應用程序中的全部用戶線程必須先中止完畢,不然JVM沒法中止

守護線程:不會影響JVM的正常中止,一般執行一些重要性不高的任務,如監視其餘線程的運行狀況

在多線程的運行中,咱們須要注意每一個段代碼是由哪個線程去負責執行的,這關係到性能問題、線程安全

System.out.println("The ** method was executed by thread: " + Thread.currentThread().getName());
複製代碼

如上能夠看看對應方法是哪一個線程負責執行的,固然你能夠創新一個新的線程,並由新的線程負責,來驗證你的猜測

Image

建立並運行

在Java中,一個線程就是一個java.lang.Thread的實例

建立一個Thread類,JVM會爲這個線程實例分配兩個調用棧(Call Stack)所需的內容空間

兩個調用棧,一個用於跟蹤Java代碼間的調用關係,另外一個用於跟蹤Java代碼對本地代碼(即Native代碼)的調用關係

假設咱們在main方法中建立了一個新的線程,那麼新的thread就是main線程的子線程,它們也就是父子線程關係

在默認狀況下父線程爲守護線程,則子線程也一樣爲守護,用戶線程也是如此,固然你也能夠經過setDaemon方法來修改這一屬性

狀態與上下文切換

人這一輩子有不少種狀態,線程也是同樣的

咱們能夠經過getState方法獲取,返回值是Enum(枚舉)

狀態 備註
NEW 有且僅有一次處於此狀態,剛建立而未啓動的線程
RUNNABLE 複合狀態,包括READY和RUNNING,當READY被JVM線程調度器調度則進入RUNNING狀態,RUNNING表示線程正在執行,即run方法代碼正在由cpu執行,當實例yield方法被調用或者線程調度器緣由,RUNNING也會轉爲READY
BLOCKED 線程發起I/O操做後或試圖去獲取其餘線程的持有鎖,則進入這個狀態,這個狀態不會佔用CPU資源,以上操做後轉爲RUNNABLE狀態
WAITING 執行某些方法(Object.wait()、Thread.join()、LockSupport.park())處於無限等待其餘線程執行特定操做的狀態,某些方法(Object.notify()、Object.notifyAll()、LockSupport.unpark(thread)讓線程從WAITING轉換爲RUNNABLE
TIMED_WAITING 處於有時間限制的等待其餘線程執行特定操做,若是其餘線程沒有執行,時間到了,就自動轉爲RUNNABLE
TERMINATED 執行結束的線程,也是僅有一次的狀態,不管成功或異常

而一個線程從RUNNABLE轉換爲BLOCKED、WAITING、TIMED_WAITING幾個狀態的過程就意味着上下文切換(Context Switch)

上下文信息:包括CPU的寄存器和程序計數器在某一時間點的內容等

就好像你在和父母打電話的時候,忽然你女友打了進來,你果斷接了女友的約會邀請,而後又從新接上父母這邊的並續上剛剛的聊天內容

從RUNNABLE轉到其餘,上下文信息(聊天內容)須要先存儲起來,而後再從新進入RUNABLE狀態時,回覆以前保存的線程的上下文信息(聊天內容),在保存和回覆的過程就是上下文切換

在保存或恢復就是須要開銷的,如CPU時間開銷和CPU緩存內容失效等

你能夠看看本身的Java程序運行時的上下文切換狀況,我這邊是win7,因此用了windows自帶的perfmon

Image

image

Linux也能夠用perf命令查看

perf stat -e cpu-clock,task-clock,cs,cache-references,cache-misses java 你的程序名
複製代碼

線程監控

我們須要把未知的東西變爲已知的,黑盒變白盒

你能夠嘗試用JDK自身的jvisualvm監視

image

或是jmc也行

image

優劣

這個其實你們都基本瞭解,因此我不打算細講來着

優點 劣勢
提供系統的吞吐量 線程安全問題
提升響應性 線程的生命特徵問題
充分利用多核CPU 上下文切換
最小化系統資源使用 可靠性
簡化程序的結構 /

關於生命特徵,可能就會涉及多種鎖

死鎖(Dead Lock):即都擁有本身的鎖可是又在等對方的鎖

T1(L1) 等 L2
T2(L2) 等 L1
複製代碼

活鎖(Live Lock):一個線程一直嘗試某個操做可是無果(就像部分暗戀、舔狗相似····,這個比喻是我和愛人說明後她給個人第一印象)

線程飢餓(Starvation):永遠沒法得到CPU執行機會,永遠處於RUNNABLE狀態的READY子狀態。

相關術語

術語 說明
任務(task) 任務是線程須要作的,不是一一對應,是一個概念,文件是任務,文件裏的多個數據也能夠是任務
併發(Concurrent) 多個任務在同一時間段內執行,不是順序執行,是交替執行
並行(Parallel) 多個任務在同一時刻執行
客戶端線程(Client Thread) 有個Hello類,它有本身的方法say(),那麼一個main函數,建立它的實體類並調用say方法,那麼對於Hello類而言,客戶端線程就是main線程
工做者線程(Worker Thread) 有個Hello類,它有本身的方法say(),它有本身的一個WorkThread線程,在初始化時,線程就開始start,相似執行本身的日誌差很少,那麼WorkThread線程就是工做者線程
上下文切換 去看上面的文章內容 :)
顯示鎖 Java代碼能夠控制的鎖,包括synchronize和java.util.concurrent.locks.Lock接口的全部類
線程安全 一段操縱共享數據的代碼可以保證在同一時間內被多個線程執行而仍然保持其正確性

下次再說說,附錄的synchronize和volatile。

我是MySelf,還在堅持學習技術與產品經理相關的知識,但願本文能給你帶來新的知識點。

公衆號:Java貓說

學習交流羣:728698035

現架構設計(碼農)兼創業技術顧問,不羈平庸,熱愛開源,雜談程序人生與不按期乾貨。

Image Text
相關文章
相關標籤/搜索