1. 程式人生 > >多線程筆記

多線程筆記

list syn 代碼 方法 避免 ror 寫鎖 生命期 要求

1.線程的6種狀態(Thread.State)

  (1)New(新創建):

  new新線程,還未運行。

  (2)Runnable(可運行)

  調用start方法後。

  (3)Blocked(被阻塞)

  當前線程試圖獲取內部的對象鎖但該鎖被其他線程持有時,該線程進入阻塞狀態;當其他線程釋放該鎖,且線程調度器允許本線程持有它的時候變成非阻塞狀態。

  (4)Waiting(等待)

  當線程等待另一個線程通知線程調度器某個條件時,它自己進入等待狀態。如執行(Object)wait、(Thread)join方法 ,或是等待java.util.concurrent庫中的Lock或Condition時。

  (5)Timed Waiting(計時等待)lock

  執行帶有超時參數的方法時,如(Thread)sleep、(Object)wait、(Thread)join、(Lock)tryLock、(Condition)await。 

  (6)Terminated(終止)

    a.run方法正常退出而自然死亡;

    b.由於未捕獲的異常導致run方法異常退出而意外死亡。

2.線程屬性

2.1線程優先級

MIN_PRIORITY=1,MAX_PRORITY=10,NORM_PRIORITY=5

當線程調度器有機會選擇新線程時,優先選擇優先級高的。

線程優先級是高度依賴於宿主系統的,Java線程優先級映射到宿主系統時可能更多也可能更少。

2.2守護線程

setDaemon(true);//為其他線程提供服務;在線程啟動之前調用

註:守護線程應該永遠不去訪問固有資源,如文件、數據庫等,因為它可能會在任何時候甚至一個操作的中間發生中斷。

2.3未捕獲異常處理器

線程的run方法不能拋出任何被檢測的異常,而未檢測異常又會導致線程終止。

不需要用catch處理可以被傳播的異常,在線程死亡之前,異常被傳遞到一個用於未捕獲異常的處理器。

3.同步

3.1原因:多線程共享同一數據

3.2實現方式

方式(一)synchronizd關鍵字

方式(二)java.util.concurrent.locks.Lock接口,實現類有:

  1. ReentrantLock
  2. ReentrantReadWriteLock.ReadLock
  3. ReentrantReadWriteLock.WriteLock

註:解鎖(unlock)語句應在finally塊中。

3.3鎖對象

Reentrantlock();

鎖是可重入的,即線程可以重復獲得已持有的鎖,鎖保持一個持有計數來跟蹤lock方法的嵌套調用。

3.4條件對象(條件變量)

原因:要用一個變量來管理那些已進入臨界區,即獲得了鎖卻不能做有用工作的線程。

Condition c=bankLock.newCondition();

當條件不滿足時,調用c.await()方法,線程進入該條件的等待集。

註意與等待鎖的線程區分:當鎖可用時,該線程不能馬上解除阻塞,相反,它要阻塞直到其他線程調用同一條件的signalAll()方法。

signal()方法隨機解除等待集中某個線程的阻塞狀態。

小結:

提供高度的鎖定控制。

  1. 鎖用來保護代碼片段,任何時刻只能有一個線程執行被保護的代碼;
  2. 鎖可以管理試圖進入被保護代碼段的線程;
  3. 鎖可以擁有一個或多個相關的條件對象;
  4. 每個條件對象管理那些已進入被保護代碼段但還不能運行的線程。

3.5 synchronized關鍵字

Java中的每個對象都有一個內部鎖,並且該鎖有一個內部條件。

wait()/notifyAll()方法相當於condition.await()/signalAll()方法。

靜態方法也可以聲明為synchronized(類對象的內部鎖)。

技術分享

所以,synchronized靜態方法有什麽意義嗎?

內部鎖和條件的局限性:

  1. 不能中斷一個正在試圖獲得鎖的線程;
  2. 試圖獲得鎖時不能設定超時;
  3. 每個鎖僅有單一條件,可能是不夠的。

建議:既不使用Lock/Condition,也不實用synchronized;推薦使用java.util.concurrent 包中的一種機制。

3.6 同步阻塞

synchronized(obj){

...

obj.method1();

obj.method2();

}

又叫客戶端鎖定,必須保證(obj)類的所有可修改方法都使用內部鎖。

因此,該方法很脆弱,不推薦使用。

3.7 監視器

在程序員不需要考慮如何加鎖的情況下保證多線程安全。

監視器的特性:

  1. 監視器是只包含私有域的類;
  2. 每個監視器類的對象有一個相關的鎖;
  3. 使用該鎖對所有的方法進行加鎖;
  4. 該鎖可以有任意多個相關條件。

Java對象的synchronized類似監視器,但不同(安全性下降):

  1. 域不要求必須是private;
  2. 方法不要求必須是synchronized;
  3. 內部鎖對客戶是可用的。

3.8 volatile域

提供一種免鎖機制;編譯器和虛擬機知道該域可能被另一個線程並發更新。

不提供原子性。

3.9 原子性

如果對共享對象除了賦值之外沒有別的操作,可以將其聲明為volatile。

3.10 死鎖

兩個或兩個以上的線程在執行過程中,因爭奪資源而產生的一種互相等待的現象。

產生死鎖的4個必要條件:

  1. 互斥條件:一個資源同時只能有一個線程訪問;
  2. 請求與保持條件:一個線程請求阻塞時,對於已獲得的資源保持不放;
  3. 不可剝奪條件:一個線程獲得的資源在只用完之前不能被剝奪,只能在使用完畢後釋放;
  4. 循環等待條件:若幹線程形成頭尾相接的循環等待資源關系。

解決死鎖的方法:

  1. 預防死鎖:設置限制條件,破壞產生死鎖的必要條件(之一);效率降低;
  2. 死鎖避免:允許前三個必要條件,但通過明智的選擇確保永遠不會到達死鎖點;比死鎖預防允許更多的並發;
  3. 死鎖檢測:不須采取任何限制措施,允許發生死鎖。但通過系統設置的檢測機構及時檢測死鎖的發生,並精確地確定死鎖相關的線程和資源,采取相關措施將死鎖清除;
  4. 死鎖解除:與死鎖檢測相配套,常用方法:撤銷或掛起一些線程,以便回收資源分配給已阻塞的線程;死鎖檢測和解除會獲得較好的資源利用率和吞吐量,但實現難。

3.11 讀寫鎖

  • java.util.concurrent.locks.ReentrantReadWriteLock

對所有的獲取方法加讀鎖——readLock()

對所有的修改方法加寫鎖——writeLock()

4. 阻塞隊列

生產者、消費者

技術分享

多線程程序中,使用不會拋出異常的poll、peek和offer方法。

  • java.util.concurrent.LinkedBlockingQueue<E> 容量無上界,但也可以指定最大容量。
  • java.util.concurrent.LinkedBlockingDeque<E> 雙端版本
  • java.util.concurrent.ArrayBlockingQueue<E> 構造時需要指定容量,並且有一個可選參數用來指定是否需要公平性。
  • java.util.concurrent.PriorityBlockingQueue<E> 是優先級隊列,而非先進先出。

5.線程安全的集合

java.util.concurrent包提供了映射表、有序集和隊列的高效實現,如:

  • java.util.concurrent.ConcurrentHashMap<K,V>
  • java.util.concurrent.ConcurrentSkipListMap<K,V>
  • java.util.concurrent.ConcurrentSkipListSet<E>
  • java.util.concurrent.ConcurrentLinkedQueue<E>

並發的散列表可以高效的支持大量讀者和一定數量的寫者;保證原子性。

6.執行器

線程池(Thread pool):程序中創建了大量生命期很短的線程時使用;減少並發線程的數目。

  將一個Runnable對象交給線程池,就會有一個線程調用run方法。

執行器(Executor)類有許多靜態工廠方法用來構建線程池。

技術分享

多線程筆記