Java多執行緒(2)執行緒鎖

天天不是小可愛發表於2019-07-09

多執行緒訪問同一個資源進行讀寫操作,就很容易出一些問題(比如我們常見的讀者寫者,生產者消費者模型)所以我們會選擇對他們設定訊號量或者加鎖,來限制同一個時刻只有一個執行緒對某個物件進行操作。

多執行緒是一個蠻複雜的工作,鎖加多了就算是看虛擬碼有的時候腦子都轉不過來,所以不要隨便加鎖(如果對自己的腦子沒太多自信的話

Synchronized

Synchronized關鍵字的作用是實現執行緒間的同步,它就像我們用PV原語來解決程式互斥問題,然後用物件的wait(),notify()提供執行緒的同步功能。

它的使用場景如下:

分類 具體方法 被鎖的物件 虛擬碼
方法 例項方法 例項物件 public synchronized void method(){}; //例項方法,鎖住的是例項物件
方法 靜態方法 類物件 public static synchronized void method(){}; //靜態方法,鎖住的是類物件
程式碼塊 例項物件 類的例項物件 synchronized(this){...};//同步程式碼塊,鎖住的是該類例項物件
程式碼塊 class物件 類的例項物件 synchronized(SynchronizedDemo.class){...}; //同步程式碼塊,鎖住的是該類的類物件
程式碼塊 任意例項物件Object 類的例項物件Object String lock=" "; synchronized(lock){...}; //同步程式碼塊,鎖住的是配置的例項物件

Attention:如果鎖住的是類物件的話,不論new多少個例項出來,因為他們是屬於一個類的,所以都會被鎖住,保證程式間同步。

從OS的原理上理解,很明顯的通過這種方式來依次排隊操作共享資源的方式是比較效率低下的。這並不是一個一個非常好的同步機制,但是它是其他併發容器的基礎。

Synchronized是怎麼實現同步的呢

在學OS的時候,有一章一直在講PV原語,Proberen申請資源訊號量-1,Verhogen釋放資源訊號量+1(Dijkstra是荷蘭人,所以就這麼任性的用荷蘭語),PV的題目都快做吐了,但是其實在真正的現代OS環境和Java當中,並不是這樣簡單的來解決問題的。

在Synchronized關鍵字的機制裡面,每個物件擁有一個monitor(計數器),當執行緒獲取該物件鎖後,計數器就會加一,釋放鎖後就會將monitor減一。(為什麼你和PV操作剛好相反啊!)

Disadvantage:

由於我們沒辦法設定synchronized關鍵字在獲取鎖的時候等待時間,所以synchronized可能會導致執行緒為了加鎖而無限期地處於阻塞狀態。
使用synchronized關鍵字等同於使用了互斥鎖,即其他執行緒都無法獲得鎖物件的訪問權。這種策略對於讀多寫少的應用而言是很不利的,因為即使多個讀者看似可以併發執行,但他們實際上還是序列的,並將最終導致併發效能的下降。

因為時間片的輪轉,導致我們在使用上覺得連貫,在一個時間週期裡面看起來像是併發進行(針對單核process而言),但實際上,效率並不高。

Q: Synchronized關鍵字和lock是一樣的嗎?它們的區別是什麼?

我覺得這兩個是不同的概念,但是我的一些同學在開發過程中把Synchronize也叫做鎖,一般說加一個物件鎖。(雖然覺得不對但是也找不到什麼反駁的理由.jpg)

看了一些部落格和書之後發現,物件鎖知識synchronized在實現鎖機制中的一類,它其實有偏向鎖,輕量鎖,自旋鎖等等,所以其實synchronized是一個鎖的封裝。

關於程式的五狀態圖這裡就不畫了,幾個狀態的切換。值得注意的是,執行緒在wait()的時候是會釋放物件的鎖的,而sleep()或者yield()是不會的

練手的Demo(使用semaphore訊號量解決讀者寫者問題)

https://github.com/JhinQaQ/ReadAndWrite

參考

《Operating Systems : Design and Implementation》
《Java併發程式設計的藝術》
《實戰Java高併發程式設計》
讓你徹底理解Synchronized https://www.jianshu.com/p/d53bf830fa09
synchronized的兩大不足 http://swiftlet.net/archives/3010
synchronized、鎖、多執行緒同步的原理是咋樣的 https://www.jianshu.com/p/5dbb07c8d5d5

相關文章