Java的線程模型建立在搶占式線程調度的基礎上,也就是說:
所有線程可以很容易的共享同一進程中的對象。能夠引用這些對象的任何線程都可以修改這些對象。為了保護數據,對象可以被鎖住。Java基于線程和鎖的并發過于底層,而且使用鎖很多時候都是很萬惡的,因為它相當于讓所有的并發都變成了排隊等待。
在Java 5以前,可以用synchronized關鍵字來實現鎖的功能,它可以用在代碼塊和方法上,表示在執行整個代碼塊或方法之前線程必須取得合適的鎖。對于類的非靜態方法(成員方法)而言,這意味這要取得對象實例的鎖,對于類的靜態方法(類方法)而言,要取得類的Class對象的鎖,對于同步代碼塊,程序員可以指定要取得的是那個對象的鎖。
不管是同步代碼塊還是同步方法,每次只有一個線程可以進入,如果其他線程試圖進入(不管是同一同步塊還是不同的同步塊),JVM會將它們掛起(放入到等鎖池中)。這種結構在并發理論中稱為臨界區(critical section)。這里我們可以對Java中用synchronized實現同步和鎖的功能做一個總結:
只能鎖定對象,不能鎖定基本數據類型被鎖定的對象數組中的單個對象不會被鎖定同步方法可以視為包含整個方法的synchronized(this) { … }代碼塊靜態同步方法會鎖定它的Class對象內部類的同步是獨立于外部類的synchronized修飾符并不是方法簽名的組成部分,所以不能出現在接口的方法聲明中非同步的方法不關心鎖的狀態,它們在同步方法運行時仍然可以得以運行synchronized實現的鎖是可重入的鎖在JVM內部,為了提高效率,同時運行的每個線程都會有它正在處理的數據的緩存副本,當我們使用synchronzied進行同步的時候,真正被同步的是在不同線程中表示被鎖定對象的內存塊(副本數據會保持和主內存的同步,現在知道為什么要用同步這個詞匯了吧),簡單的說就是在同步塊或同步方法執行完后,對被鎖定的對象做的任何修改要在釋放鎖之前寫回到主內存中;在進入同步塊得到鎖之后,被鎖定對象的數據是從主內存中讀出來的,持有鎖的線程的數據副本一定和主內存中的數據視圖是同步的 。
基于synchronized關鍵字的鎖機制有以下問題:
鎖只有一種類型,而且對所有同步操作都是一樣的作用鎖只能在代碼塊或方法開始的地方獲得,在結束的地方釋放線程要么得到鎖,要么阻塞,沒有其他的可能性Java 5對鎖機制進行了重構,提供了顯示的鎖,這樣可以在以下幾個方面提升鎖機制:
可以添加不同類型的鎖,例如讀取鎖和寫入鎖可以在一個方法中加鎖,在另一個方法中解鎖可以使用tryLock方式嘗試獲得鎖,如果得不到鎖可以等待、回退或者干點別的事情,當然也可以在超時之后放棄操作顯示的鎖都實現了java.util.concurrent.Lock接口,主要有兩個實現類:
ReentrantLock – 比synchronized稍微靈活一些的重入鎖ReentrantReadWriteLock – 在讀操作很多寫操作很少時性能更好的一種重入鎖Reentrant 鎖意味著什么呢?
簡單來說,它有一個與鎖相關的獲取計數器,如果擁有鎖的某個線程再次得到鎖,那么獲取計數器就加1,然后鎖需要被釋放兩次才能獲得真正釋放。這模仿了 synchronized 的語義;如果線程進入由線程已經擁有的監控器保護的 synchronized 塊,就允許線程繼續進行,當線程退出第二個(或者后續) synchronized 塊的時候,不釋放鎖,只有線程退出它進入的監控器保護的第一個 synchronized 塊時,才釋放鎖。
本文節選自:關于Java并發編程的總結和思考
作者:jiankunking 出處:http://blog.csdn.net/jiankunking
新聞熱點
疑難解答