一、線程的先來後到
我們來舉一個Dirty的例子:某餐廳的浴室很小,幾乎只能容納一個人如廁。為了確保不受干擾,如廁的人進入衛生間,就要鎖上房門。我們可以把浴室想像成是共享的資源,而眾多需要如廁的人可以被視為多個線程。假如衛生間目前有人佔用,那麼其他人必須等待,直到這個人如廁完畢,打開房門走出來為止。這就好比多個執行緒共享一個資源的時候,是一定要分出先來後到的。
有人說:那如果我沒有這道門會怎麼樣呢?讓兩個線程互相競爭,誰搶先了,誰就可以先工作,這樣多好阿?但我們知道:如果廁所沒有門的話,如廁的人一起湧向廁所,那麼必然會發生爭執,正常的如廁步驟就會被打亂,很有可能會發生意想不到的結果,例如某些人可能只好被迫在不正確的地方施肥……
正是因為有這道門,任何一個單獨進入如廁的人都可以順利的完成他們的如廁過程,而不會被幹擾,甚至發生以外的結果。這就是說,如廁的時候要講究先來後到。
那麼在Java 多執行緒程式當中,當多個執行緒競爭同一個資源的時候,如何能夠保證他們不會產生「打架」的情況呢?有人說是用同步機制。沒錯,像上面這個例子,就是典型的同步案例,一旦***位開始如廁,則第二位必須等待***位元結束,才能開始他的如廁過程。一個線程,一旦進入某一過程,就必須等待正常的返回,並退出這個過程,下一個線程才能開始這個過程。
這裡,最關鍵的就是浴室的門。其實,衛生間的門擔任的是資源鎖的角色,只要如廁的人鎖上門,就相當於獲得了這個鎖,而當他打開鎖出來以後,就相當於釋放了這個鎖。
也就是說,多執行緒的執行緒同步機制其實是靠鎖的概念來控制的。那麼在Java程式當中,鎖是如何體現的呢?
讓我們從JVM的角度來看鎖定這個概念:
在Java程式執行時間環境中,JVM需要對兩類執行緒共享的資料進行協調:
1)保存在堆中的實例變數
2)保存在方法區中的類別變數
這兩類資料是被所有執行緒共享的。 (程式不需要協調保存在Java 堆疊當中的資料。因為這些資料是屬於擁有該堆疊的執行緒所私有的。)
在java虛擬機器中,每個物件和類別在邏輯上都是和一個監視器相關聯的。
對於物件來說,相關聯的監視器保護物件的實例變數。
對於類別來說,監視器保護類別的類別變數。 (如果一個物件沒有實例變量,或者一個類別沒有變量,相關聯的監視器就什麼也不監視。)
為了實現監視器的排他性監視能力,java虛擬機為每一個物件和類別都關聯一個鎖。代表任何時候只允許一個執行緒擁有的特權。執行緒存取實例變數或類別變數不需鎖。
但是如果執行緒取得了鎖,那麼在它釋放這個鎖之前,就沒有其他執行緒可以取得相同資料的鎖了。 (鎖住一個物件就是取得物件相關聯的監視器)
類別鎖實際上用物件鎖來實現。當虛擬機器裝載一個class檔案的時候,它就會建立一個java.lang.Class類別的實例。當鎖住一個物件的時候,實際上鎖住的是那個類別的Class物件。
一個執行緒可以多次對同一個物件上鎖。對於每一個對象,java虛擬機維護一個加鎖計數器,線程每獲得一次該對象,計數器就加1,每釋放一次,計數器就減 1,當計數器值為0時,鎖就被完全釋放了。
java程式設計人員不需要自己動手加鎖,物件鎖是java虛擬機器內部使用的。
在java程式中,只需要使用synchronized區塊或synchronized方法就可以標誌一個監視區域。當每次進入一個監視區域時,java 虛擬機器都會自動鎖定物件或類別。
以上是Java中多執行緒同步怎麼理解的詳細內容。更多資訊請關注PHP中文網其他相關文章!