(初識併發)線程的概念?如何快速認識併發

寫在前面

重新去學習併發編程,這邊文章帶大家重新去整理線程的一些基本知識以及 JAVA 中線程的創建。當然了也適合要學習怎麼去使用多線程編程的同學們。

怎麼去學習(小tips)?

在準備去學習和理解相關概念的時候,這裏有個小技巧分享給大家。就是在接觸概念時,我們首先腦海中需要有一副多線程的執行圖:
多線程原理圖
所謂的多線程,其實就是在程序執行的過程中,分裂出多個線程去執行。但是正因爲分裂後的多線程去同時執行任務,可能會導致一些資源搶佔、先後順序等等很多問題。所以設計出了各種各種的東西來控制它。以及檢測它。

1. 併發的一些專業術語

1.1 線程安全

一個類多個線程以任何一種方式同時調用,且不需要外部額外同步和協同的情況下,仍然保持內部數據正確表現正確的行爲,那麼類就是線程安全的。

1.2 線程安全等級

在這裏插入圖片描述

1.2.1 不可變

不可變的對象一定是線程安全的。如:final修飾的不可變類(String,Integer等);enum枚舉類
注:enum編譯後其實就是生成一堆 final 類,所以不可變

final 解釋
  1. final是當你創建一個對象時,使用final關鍵字能夠使得另外一個線程不會訪問到處於“部分創建”的對象,否則不能保證線程安全
  2. final 只是用來保證值不能被直接覆蓋的。

1.2.2 線程安全類

類中的任意方法都不會使得數據處於不一致或者數據污染的情況。java.util.concurrent包下的類。

1.2.3 有條件的線程安全

對於單獨訪問類的安全,是線程安全。但是對於某些複合操作,需要外部類來同步。

1.2.4 線程對立類

線程對立類是那些不管是否調用了外部同步都不能 在併發使用時保證其安全的類。

1.3 同步異步、阻塞非阻塞

1.3.1 同步的概念

阻塞式調用,調用必須等待響應對方執行完畢纔會返回。

1.3.2 異步的概念

非阻塞式調用,立即返回,調用方無需等待響應返回實際結果,響應方會通過狀態、通知或回調來告知調用方。

3.2.1 應用場景

  1. 耗時任務。主線程提交耗時任務到線程池,通過 Feture 來異步獲取任務執行結果。
  2. 電商下單鏈路的非核心鏈路調用:爲下單的性能考慮,將訂單下發的發貨倉庫等非實時的流程放在後續操作,提高下單的響應速度。

1.4. 併發並行

1.4.1 併發

一個線程處理多個事件,在時間線上,通過切換來達到效果。

1.4.2 並行

多個處理器處理多個事件。

2. JAVA 中的線程,以及怎麼去創建線程的?

在java中,每一條線程就是 Thread 類。

2.1. 線程的狀態

NEW: 被創建但是未運行,未調用 start() 方法
RUNNABLE: 可被執行(正在執行RUNNING、在JVM裏面在等待執行READY)
BLOCKED: 阻塞,正在等待監視器鎖,來進入或重入synchronized代碼塊或方法
WAITING: 被執行 wait() 的等待
TIMEWAITING: 時間等待
ERMINATED: 終結狀態,已經執行完成
在這裏插入圖片描述

2.2 Thread 類中部分方法講解:

Threard.yield()
線程讓步,當前線程退出時間片,讓其他線程活當前線程使用CPU時間片執行
Thread.sleep()
線程休眠,主動讓出當前CPU時間,在指定時間過後,CPU會返回繼續執行該線程。但是! sleep 方法不會釋放當前鎖持有的鎖。
Thread.join()
等待該線程死亡/終止,當前線程會等待調用該方法的線程執行完後才繼續執行。
Object.wait()
Object 類的方法,調用前必須擁有對象鎖,例如在synchroized代碼塊內,調用wait方法後,對象鎖會釋放,線程進入 WAITING 等待狀態。

爲什麼會有這些方法?
其實就是爲了在 主線程 執行過程中,對多線程之間執行的順序進行控制。如果單單從多個new出來的線程進行分析,是很容易懵的。在理解概念的時候,我們應該把 程序主線程 給考慮進去,這樣會容易理解一點。

3. 併發的問題:死鎖

兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信二造成的阻塞現象。沒有外力作用,它們將無法推進下去。(簡單的描述爲:兩個線程/進程佔有着資源,卻還想去拿對方正在佔用的資源。打個比方兩個小孩自己手上拿着玩具不放手,又嚷嚷着要拿對方手裏的玩具。此時就會出現死鎖。特點就是如果沒有人出來幫忙,就會一直死鎖下去,互不讓步)
在這裏插入圖片描述

3.1 死鎖原因

兩個及以上的線程,搶佔2把及以上的鎖,搶佔鎖的順序不一致。

3.2 避免和處理死鎖思路

1)不使用鎖,不使用2把以及2把以上的鎖
2)必須使用2把或2把以上的鎖時,必須保證獲取鎖的順序是一致的
3)嘗試獲得具有超時釋放的鎖,例如 Lock 中的tryLock來獲取鎖
4)當發生了 JAVA-level 的鎖時,重啓程序來幹掉進程/線程

3.3 如何定位死鎖的位置?

1) JPS 拿到 pid
2) jstack 拿到JVM線程的狀態,通過狀態 或 看看有沒有相關關鍵字(java-deadlock)關鍵字

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