1. 程式人生 > >程式設計思想之多執行緒與多程序(2)——執行緒優先順序與執行緒安全

程式設計思想之多執行緒與多程序(2)——執行緒優先順序與執行緒安全

程式設計思想之多執行緒與多程序(1)——以作業系統的角度述說執行緒與程序》一文詳細講述了執行緒、程序的關係及在作業系統中的表現,這是多執行緒學習必須瞭解的基礎。本文將接著講一下執行緒優先順序和執行緒安全。

執行緒優先順序

現在主流作業系統(如Windows、Linux、Mac OS X)的任務排程除了具有前面提到的時間片輪轉的特點外,還有優先順序排程(Priority Schedule)的特點。優先順序排程決定了執行緒按照什麼順序輪流執行,在具有優先順序排程的系統中,執行緒擁有各自的執行緒優先順序(Thread Priority)。具有高優先順序的執行緒會更早地執行,而低優先順序的執行緒通常要等沒有更高優先順序的可執行執行緒時才會被執行。

執行緒的優先順序可以由使用者手動設定,此外系統也會根據不同情形調整優先順序。通常情況下,頻繁地進入等待狀態(進入等待狀態會放棄之前仍可佔用的時間份額)的執行緒(如IO執行緒),比頻繁進行大量計算以至於每次都把所有時間片全部用盡的執行緒更受作業系統的歡迎。因為頻繁進入等待的執行緒只會佔用很少的時間,這樣作業系統可以處理更多的任務。我們把頻繁等待的執行緒稱之為IO密集型執行緒(IO Bound Thread),而把很少等待的執行緒稱之為CPU密集型執行緒(CPU Bound Thread)。IO密集型執行緒總是比CPU密集型執行緒更容易得到優先順序的提升。

執行緒餓死:

在優先順序排程下,容易出現一種執行緒餓死的現象。一個執行緒餓死

是說它的優先順序較低,在它執行之前總是有比它優先順序更高的執行緒等待執行,因此這個低優先順序的執行緒始終得不到執行。當CPU密集型的執行緒優先順序較高時,其它低優先順序的執行緒就很可能出現餓死的情況;當IO密集型執行緒優先順序較高時,其它執行緒相對不容易造成餓死的善,因為IO執行緒有大量的等待時間。為了避免執行緒餓死,排程系統通常會逐步提升那些等待了很久而得不到執行的執行緒的優先順序。這樣,一個執行緒只要它等待了足夠長的時間,其優先順序總會被提升到可以讓它執行的程度,也就是說這種情況下執行緒始終會得到執行,只是時間的問題。

在優先順序排程環境下,執行緒優先順序的改變有三種方式:
1. 使用者指定優先順序;
2. 根據進入等待狀態的頻繁程度提升或降低優先順序(由作業系統完成);
3. 長時間得不到執行而被提升優先順序。

執行緒安全與鎖

在多個執行緒併發執行訪問同一個資料時,如果不採取相應的措施,將會是非常危險的。假設你在工行有一個銀行賬戶,兩張銀聯卡(自己手裡一張,女朋友手裡一張),裡面有100萬。假設取錢就兩個過程:1.檢查賬戶餘額,2.取出現金(如果要取出的金額 > 賬戶餘額,則取現成功,否則取現失敗)。有一天你要買房想把錢取出來,而此時你女朋友也想買一輛車(假設你們事先沒有商量)。兩個人都在取錢,你在A號ATM機取100萬,女朋友在B號ATM機取80萬。這時A號ATM檢查賬戶餘額發現有100萬,可以取出;而與此同時,同一時刻B號ATM也在檢查賬戶餘額發現有100萬,可以取出;這樣,A、B都把錢取出來了。

100萬的存款取出180萬,銀行就虧大發了(當然你就笑呵呵了……)!這就是執行緒併發的不安全性。為避免這種情況發生,我們要將多個執行緒對同一資料的訪問同步,確保執行緒安全。

所謂同步(synchronization)就是指一個執行緒訪問資料時,其它執行緒不得對同一個資料進行訪問,即同一時刻只能有一個執行緒訪問該資料,當這一執行緒訪問結束時其它執行緒才能對這它進行訪問。同步最常見的方式就是使用鎖(Lock),也稱為執行緒鎖。鎖是一種非強制機制,每一個執行緒在訪問資料或資源之前,首先試圖獲取(Acquire)鎖,並在訪問結束之後釋放(Release)鎖。在鎖被佔用時試圖獲取鎖,執行緒會進入等待狀態,直到鎖被釋放再次變為可用。

二元訊號量

二元訊號量(Binary Semaphore)是一種最簡單的鎖,它有兩種狀態:佔用和非佔用。它適合只能被唯一一個執行緒獨佔訪問的資源。當二元訊號量處於非佔用狀態時,第一個試圖獲取該二元訊號量鎖的執行緒會獲得該鎖,並將二元訊號量鎖置為佔用狀態,之後其它試圖獲取該二元訊號量的執行緒會進入等待狀態,直到該鎖被釋放。

訊號量

多元訊號量允許多個執行緒訪問同一個資源,多元訊號量簡稱訊號量(Semaphore),對於允許多個執行緒併發訪問的資源,這是一個很好的選擇。一個初始值為N的訊號量允許N個執行緒併發訪問。執行緒訪問資源時首先獲取訊號量鎖,進行如下操作:
1. 將訊號量的值減1;
2. 如果訊號量的值小於0,則進入等待狀態,否則繼續執行;
訪問資源結束之後,執行緒釋放訊號量鎖,進行如下操作:
1. 將訊號量的值加1;
2. 如果訊號量的值小於1(等於0),喚醒一個等待中的執行緒;

互斥量

互斥量(Mutex)和二元訊號量類似,資源僅允許一個執行緒訪問。與二元訊號量不同的是,訊號量在整個系統中可以被任意執行緒獲取和釋放,也就是說,同一個訊號量可以由一個執行緒獲取而由另一執行緒釋放。而互斥量則要求哪個執行緒獲取了該互斥量鎖就由哪個執行緒釋放,其它執行緒越俎代庖釋放互斥量是無效的。

臨界區

臨界區(Critical Section)是一種比互斥量更加嚴格的同步手段。互斥量和訊號量在系統的任何程序都是可見的,也就是說一個程序建立了一個互斥量或訊號量,另一程序試圖獲取該鎖是合法的。而臨界區的作用範圍僅限於本程序,其它的程序無法獲取該鎖。除此之處,臨界區與互斥量的性質相同。

讀寫鎖

讀寫鎖(Read-Write Lock)允許多個執行緒同時對同一個資料進行讀操作,而只允許一個執行緒進行寫操作。這是因為讀操作不會改變資料的內容,是安全的;而寫操作會改變資料的內容,是不安全的。對同一個讀寫鎖,有兩種獲取方式:共享的(Shared)和獨佔的(Exclusive)。當鎖處於自由狀態時,試圖以任何一種方式獲取鎖都能成功,並將鎖置為對應的狀態;如果鎖處於共享狀態,其它執行緒以共享方式獲取該鎖,仍然能成功,此時該鎖分配給了多個執行緒;如果其它執行緒試圖如獨佔的方式獲取處於共享狀態的鎖,它必須等待所有執行緒釋放該鎖;處於獨佔狀態的鎖阻止任何執行緒獲取該鎖,不論它們以何種方式。獲取讀寫鎖的方式總結如下:

讀寫鎖的狀態 以共享方式獲取 以獨佔方式獲取
自由 成功 成功
共享 成功 等待
獨佔 等待 等待
表 1 :獲取讀寫鎖的方式


如果您有什麼疑惑和想法,請在評論處給予反饋,您的反饋就是最好的測評師!由於本人技術和能力有限,如果本博文有錯誤或不足之處,敬請諒解並給出您寶貴的建議!


========================程式設計思想系列文章回顧========================
========================程式設計思想系列文章回顧========================
程式設計思想之多執行緒與多程序
程式設計思想之訊息機制
程式設計思想之日誌記錄
程式設計思想之異常處理
程式設計思想之正則表示式
程式設計思想之迭代器
程式設計思想之遞迴
程式設計思想之回撥

相關推薦

程式設計思想執行程序系列(上)

什麼是執行緒 什麼是執行緒?執行緒與程序與有什麼關係?這是一個非常抽象的問題,也是一個特別廣的話題,涉及到非常多的知識。我不能確保能把它講的話,也不能確保講的內容全部都正確。即使這樣,我也希望儘可能地把他講通俗一點,講的明白一點,因為這是個一直困擾我很久的,撲朔迷離的知識領

程式設計思想執行程序(2)——執行優先順序執行安全

《程式設計思想之多執行緒與多程序(1)——以作業系統的角度述說執行緒與程序》一文詳細講述了執行緒、程序的關係及在作業系統中的表現,這是多執行緒學習必須瞭解的基礎。本文將接著講一下執行緒優先順序和執行緒安全。 執行緒優先順序 現在主流作業系統(

非同步程式設計學習路(三)-執行之間的協作通訊

本文是非同步程式設計學習之路(三)-多執行緒之間的協作與通訊,若要關注前文,請點選傳送門: 非同步程式設計學習之路(二)-通過Synchronize實現執行緒安全的多執行緒 通過前文,我們學習到如何實現同步的多執行緒,但是在很多情況下,僅僅同步是不夠的,還需要執行緒與執行緒協作(通訊),生產

程式設計思想 型、初始化順序、協變返回型別」

溫馨提示:本系列博文(含示例程式碼)已經同步到 GitHub,地址為「java-skills」,歡迎感興趣的童鞋Star、Fork,糾錯。 在面向物件的程式語言中,有三個特性,分別為:封裝、繼承和多型。實現多型的前提是繼承,多型的作用是消除型別之間的耦

Java程式設計思想學習(一)----物件導論中型的理解

1.1抽象過程 1)萬物皆物件。 2)程式是物件的集合,他們通過傳送訊息來告知彼此所要求做的。 3)每個物件都有自己的由其他物件所構成的儲存。 4)每個物件都擁有其型別。 5)某一特定型別的所有物件都可以接收同樣的訊息。 上面是書上總結的內容,具體程式碼如下: //每個物件都有一個介面,介

java程式設計思想控制執行流程

java 使用了c的所有流程控制語句,涉及的關鍵字包括 if-else、while、do - while 、for 、 return 、break以及選擇語句switch。 1、true和false 所有條件語句都利用條件表示式的真或假來決定執行路徑 2、 if - elseif -

JAVA程式設計思想學習筆記(七)

多型 繫結 繫結: 將一個方法呼叫同一個方法主體關聯起來被稱作繫結。 前期繫結: 若在程式執行前進行繫結,叫做前期繫結,它是面嚮物件語言不需要選擇就預設的繫結方式。 後期繫結: 它的含義就是在執行時根據物件的型別進行繫結,也叫做動態繫結或執行時繫結。java中除了static和fin

Java程式設計思想 第八章讀書筆記-

多型是面向物件三種基本特徵之一(繼承,抽象,多型) 多型存在的意義是什麼?自己的理解,多型消除了型別的耦合。如果一個方法的引數是基類物件,如果沒有多型,那麼我們需要判斷傳入的引數到底是基類的哪個衍生類,然後還掉統一方法名的方法,程式碼比較冗餘,和類的耦合度很高。有了多型,我

Java程式設計思想清理初始化

Java資料儲存 物件存在何處,C++認為效率控制是最重要的議題,所以給程式設計師提供了選擇的權利。為了追求最大的執行速度,物件的儲存空間與生命週期可以在編寫程式時確定,可以將物件置於堆疊,限域變數或者靜態儲存區。 第二種方式是在被稱為堆的記憶體池中動態建立

程式設計思想冪等性一程式設計

  J前言   今年年初遇到專案災難,解決了不少問題,這是其中一個問題。很早的時候寫的,學以致用的。今天看到還有這樣一篇稿文,那就整理下分享給大家學習!程式設計思想之冪等性   什麼是冪等性   既然冪等性源於數學,那我就使用數學公式來表示,即可一目瞭然!   f(f(x))

程式設計思想遞迴

我之前寫過關於遞迴演算法的博文,但作為程式設計思想系列的文章不得不再對它進行進一步深入的剖析。因為它是一種簡單、常用又重要的一種程式設計思想。什麼叫遞迴?舉一個通俗的例子:有一個8倆重的蘋果要你切成重量

Java程式設計思想讀書筆記系列四 --- 第六章 --- 訪問許可權控制

這是Java程式設計思想之讀書筆記系列的第四篇,主要記錄了第六章訪問許可權控制的相關內容。具體內容如下:面向物件設計中需要考慮的一個基本問題:如何把變動的事物與保持不變的事物區分開來Java提供了訪問許

Java程式設計思想讀書筆記系列九 --- 第十一章 --- 持有物件

基本的容器類:List, Set, Queue和MapJava容器類都可以自動地調整自己的尺寸可以使用註解來抑制警告資訊:註解以“@”開頭,可以接收引數,比如 @SuppressWarnings("unchecked")表示只有有關“不受檢查的異常”的警告資訊應該被抑制容器類的尖括號括起來的是引數型別,可以有

Java程式設計思想通過異常處理錯誤

1.     異常分為被檢查的異常和執行時異常,被檢查的異常在編譯時被強制要求檢查。異常被用來錯誤報告和錯誤恢復,但很大一部分都是用作錯誤報告的。 2.     異常情形是由於當前環境下無法得到必要的資訊導致當前方法或作用域無法繼續執行。當丟擲異常時,首先在堆上建立了異常物

Java程式設計思想讀書筆記系列十一 --- 第十三章 --- 字串

String物件是不可變的,具有隻讀特性 預先指定StringBuilder的大小可以避免多次重新分配緩衝(那麼:如果超出預先指定的大小,會出現什麼情況呢?) 重寫自定義類的toString()方法

Java程式設計思想二十 併發

20.1 併發得多面性 併發程式設計令人困惑的一個主要原因:使用併發時需要解決的問題有多個,而實現併發的方法也有多種,並且在這兩者之間沒有明顯的對映關係。 20.1.1 更快的執行 速度問題初聽起來很簡單:如果你需要一個程式執行得更快,那麼可以將起斷開為多個片段,在單個處理器上執行每個片段。 併發通常是提高執

SpringCloud(三)Eureka服務註冊發現《2》(actuatorEureka自我保護)

1、actuator與註冊微服務資訊完善 1.1、主機名稱:服務名稱修改 1.當前問題:含有主機名或主機ip。 2.修改microservice-provider-dept-8001yml檔案: eureka:   client:&nbs

RabbitMQ.net core(四) 訊息的優先順序 死信佇列

1.訊息的優先順序 假如現在有個需求,我們需要讓一些優先順序最高的通知推送到客戶端,我們可以使用redis的sortedset,也可以使用我們今天要說的rabbit的訊息優先順序屬性 Producer程式碼 using RabbitMQ.Client; using System; using

Java執行學習筆記21單例模式執行

詳細程式碼見:github程式碼地址   第六章 單例模式與多執行緒 前言: 我之前已經開設了23個設計模式這個專欄,介紹了很多的Java設計模式,其中一些模式對於絕 大多數程式語言設計思想都是類似的,需要了解單例模式的可以去看看。 我們在實際開發中經常用到單例模式,但

Python併發程式設計系列執行

1引言 2 建立執行緒   2.1 函式的方式建立執行緒   2.2 類的方式建立執行緒 3 Thread類的常用屬性和方法   3.1 守護執行緒:Deamon   3.2 join()方法 4 執行緒間的同步機制   4.1 互斥鎖:Lock   4.2 遞迴鎖:RLock   4.3