多執行緒程式設計為程式開發帶來了很多的方便,但是也帶來了一些問題,這些問題是在程式開發過程中必須進行處理的問題。
這些問題的核心是,如果多個執行緒同時訪問乙個資源,例如變數、檔案等,時如何保證訪問安全的問題。在多執行緒程式設計中,這種會被多個執行緒同時訪問的資源叫做臨界資源。
下面通過乙個簡單的示例,演示多個執行緒訪問臨界資源時產生的問題。在該示例中,啟動了兩個執行緒類
datathread
的物件,該執行緒每隔
200毫秒輸出一次變數
n的值,並將
n的值減少
1。變數
n的值儲存在模擬臨界資源的
data
類中,該示例的核心是兩個執行緒類都使用同乙個
data
類的物件,這樣
data
類的這個物件就是乙個臨界資源了。示例**如下:
package syn1;
/***
模擬臨界資源的類 */
public class data }
package syn1;
/***
測試多執行緒訪問時的問題 */
public class testmulthread1 }
package syn1;
/***
訪問資料的執行緒 */
public class datathread extends thread
public void run()
}catch(exception e){}}}
在執行時,因為不同情況下該程式的執行結果會出現不同,該程式的一種執行結果為: 執行緒
1:60
執行緒2:60
執行緒2:58
執行緒1:58
執行緒2:56
執行緒1:56
執行緒2:54
執行緒1:54
執行緒2:52
執行緒1:52
執行緒2:50
執行緒1:50
執行緒2:48
執行緒1:48
執行緒2:47
執行緒1:46
執行緒2:44
執行緒1:44
執行緒2:42
執行緒1:42
從執行結果來看,第一次都輸出
60是可以理解的,因為執行緒在執行時首先輸出變數的值,這個時候變數
n的值還是初始值
60,而後續的輸出就比較麻煩了,在開始的時候兩個變數保持一致的輸出,而不是依次輸出
n的每個值的內容,而到將要結束時,執行緒2輸出
47這個中間數值。
出現這種結果的原因很簡單:執行緒
1改變了變數
n的值以後,還沒有來得及輸出,這個變數
n的值就被執行緒
2給改變了,所以在輸出時看的輸出都是跳躍的,偶爾出現了連續。
出現這個問題也比較容易接受,因為最基本的多執行緒程式,系統只保證執行緒同時執行,至於哪個先執行,哪個後執行,或者執行中會出現乙個執行緒執行到一半,就把
cpu的執行權交給了另外乙個執行緒,這樣執行緒的執行順序是隨機的,不受控制的。所以會出現上面的結果。
這種結果在很多實際應用中是不能被接受的,例如銀行的應用,兩個人同時取乙個賬戶的存款,乙個使用存摺、乙個使用卡,這樣訪問賬戶的金額就會出現問題。或者是售票系統中,如果也這樣就出現有人買到相同座位的票,而有些座位的票卻未售出。
在多執行緒程式設計中,這個是乙個典型的臨界資源問題,解決這個問題最基本,最簡單的思路就是使用同步關鍵字
synchronized。
synchronized
關鍵字是乙個修飾符,可以修飾方法或**塊,其的作用就是,對於同乙個物件
(不是乙個類的不同物件
),當多個執行緒都同時呼叫該方法或**塊時,必須依次執行,也就是說,如果兩個或兩個以上的執行緒同時執行該段**時,如果乙個執行緒已經開始執行該段**,則另外乙個執行緒必須等待這個執行緒執行完這段**才能開始執行。就和在銀行的櫃檯辦理業務一樣,營業員就是這個物件,每個顧客就好比執行緒,當乙個顧客開始辦理時,其它顧客都必須等待,及時這個正在辦理的顧客在辦理過程中接了乙個**
(模擬於這個執行緒釋放了占用
cpu的時間,而處於阻塞狀態
),其它執行緒也只能等待。 使用
synchronized
關鍵字修改以後的上面的**為:
package syn2;
/***
模擬臨界資源的類 */
public class data2
public synchronized void action(string name)}
package syn2;
/***
測試多執行緒訪問時的問題 */
public class testmulthread2 }
package syn2;
/***
訪問資料的執行緒 */
public class data2thread extends thread
public void run()
}catch(exception e){}}}
該示例**的執行結果會出現不同,一種執行結果為: 執行緒
1:60
執行緒2:59
執行緒2:58
執行緒1:57
執行緒2:56
執行緒1:55
執行緒2:54
執行緒1:53
執行緒2:52
執行緒1:51
執行緒2:50
執行緒1:49
執行緒1:48
執行緒2:47
執行緒2:46
執行緒1:45
執行緒2:44
執行緒1:43
執行緒2:42
執行緒1:41
在該示例中,將列印變數
n的**和變數
n變化的**組成乙個專門的方法
action
,並且使用修飾符
synchronized
修改該方法,也就是說對於乙個
data2
的物件,無論多少個執行緒同時呼叫
action
方法時,只有乙個執行緒完全執行完該方法以後,別的執行緒才能夠執行該方法。這就相當於乙個執行緒執行到該物件的
synchronized
方法時,就為這個物件加上了一把鎖,鎖住了這個物件,別的執行緒在呼叫該方法時,發現了這把鎖以後就繼續等待下去了。
其實這只是一種簡單的方法,使用這種方法雖然解決了同時訪問的問題,但是卻使得執行緒的執行是無序的,從執行結果可以看出,執行緒的執行是交錯的。就好象在銀行辦理業務一樣,一大群人圍在視窗,乙個人在辦理時其他的人就處於等待,但是當這個人辦理完成以後其他的人那個先辦理就看誰能搶到了。那如果想讓執行緒有序執行,就是讓執行緒執行時依次執行,就像銀行中排隊辦理業務一樣,又該怎麼解決呢?這個就是執行緒的另外一種解決思路了,比較著名的例子是生產者
-消費者模型,這裡就不再重複了,下面舉乙個淺顯的例子來說明這個問題,並使用**實現該示例。
Java程式設計那些事兒100 多執行緒問題及處理3
多執行緒程式設計在實際的網路程式開發中,在客戶端程式實現中使用的比較簡單,但是在伺服器端程式實現中卻不僅是大量使用,而且會出現比客戶端更多的問題。另 外乙個容易在伺服器端出現的多執行緒問題是 死鎖。死鎖指兩個或兩個以上的執行緒為了使用某個臨界資源而無限制的等待下去。還是以前面衛生間的例子來說明死 鎖...
Java程式設計那些事兒 如何學好程式設計
俗話說 興趣是最好的老師 但是只靠興趣是遠遠不夠的,還需要付出艱辛的努力。程式設計是一種技能,需要在較短的時間內學會,就不能像學習漢語一樣,通過十幾年甚至更長的時間來學好,也不能像英語那樣進行業餘學習,以至很多大學畢業的人英語水平也不敢恭維,也達不到實用的程度。那麼如何學好程式設計呢?或者更現實一點...
java多執行緒那些事
多執行緒那些事 0.多執行緒實現兩種方式 1 new thread public void run start 2 new thread new runnable start 推薦第二種,物件導向,1.synchronzied加誰身上鎖住誰,而且還是個悲觀鎖,但是確實能解決多執行緒同步問題,就是效率...