1、多線程 synchronized volatile

線程安全概念:當多個線程訪問某一個類(對象或方法)時,這個對象始終都能表現出正確的行爲,那麼這個類(對象或方法)就是線程安全的。

  

  

synchronized:可以在任意對象及方法上加鎖,而加鎖的這段代碼稱爲"互斥區"或"臨界區"

  

示例:MyThread.java

總結:當多個線程訪問myThread的run方法時,以排隊的方式進行處理(這裏排對是按照CPU分配的先後順序而定的),一個線程想要執行synchronized修飾的方法裏的代碼:

* 1 嘗試獲得鎖

* 2 如果拿到鎖,執行synchronized代碼體內容;拿不到鎖,這個線程就會不斷的嘗試獲得這把鎖,直到拿到爲止, 而且是多個線程同時去競爭這把鎖。(也就是會有鎖競爭的問題)

  

多個線程多個鎖:多個線程,每個線程都可以拿到自己指定的鎖,分別獲得鎖之後,執行synchronized方法體的內容。

示例:MultiThread.java

總結:關鍵字synchronized取得的鎖都是對象鎖,而不是把一段代碼(方法)當做鎖, 所以代碼中哪個線程先執行synchronized關鍵字的方法,哪個線程就持有該方法所屬對象的鎖(Lock),在靜態方法上加synchronized關鍵字,表示鎖定.class類,類一級別的鎖(獨佔.class類)。

  

1.3 對象鎖的同步和異步

同步:synchronized

同步的概念就是共享,我們要牢牢記住"共享"這倆個字,如果不是共享的資源,就沒有必要進行同步。

異步:asynchronized

異步的概念就是獨立,相互之間不受到任何制約。就好像我們學習h即的時候,在頁面發起的Ajax請求,我們還可以繼續瀏覽或操作頁面的內容,二者之間沒有任何關係。

同步的目的就是爲了線程安全,其實對於線程安全來說,需要滿足倆個特性:

原子性(同步)

可見性

示例總結:

A線程先持有object對象的Lock鎖,B線程如果在這個時候調用對象中的同步(synchronized)方法則需等待,也就是同步

A線程先持有object對象的Lock鎖,B線程可以以異步的方式調用對象中的非synchronized修飾的方法

  

示例:MyObject .java

  

1.4 髒讀

對於對象的同步和異步的方法,我們在設計自己的程序的時候,一定要考慮問題的整體,不然就會出現數據不一致的錯誤,很經典的錯誤就是髒讀(dirtyread)

示例總結:

在我們對一個對象的方法加鎖的時候,需要考慮業務的整體性,即爲

setValue/getValue方法同時加鎖synchronized同步關鍵字,保證業務(service)的原子性,不然會出現業務錯誤(也從側面保證業務的一致性)。

  

示例:DirtyRead .java

  

1.5 synchronized鎖重入:

關鍵字synchronized擁有鎖重入的功能,也就是在使用synchronized時,當一

個線程得到了一個對象的鎖後,再次請求此對象時是可以再次得到該對象的鎖。

出現異常,鎖自動釋放:

說明:對於web應用程序,異常釋放鎖的情況,如果不及時處理,很可能對你的應用程序業務邏輯產生嚴重的錯誤,比如你現在執行一個隊列任務,很多對象都去在等待第一個對象正確執行完畢再去釋放鎖,但是第一個對象由於異常的出現,導致業務邏輯沒有正常執行完畢,就釋放了鎖,那麼可想而知後續的對象執行的都是錯誤的邏輯。所以這一點一定要引起注意,在編寫代碼的時候,一定要考慮周全。

示例: SyncDubbo1 SyncDubbo2 SyncException

  

1.6 synchronized代碼塊

使用synchronized聲明的方法在某些情況下是有弊端的,比如A線程調用同步的方法執行一個很長時間的任務,那麼B線程就必須等待比較長的時間才能執行,這樣的情況下可以使用synchronized代碼塊去優化代碼執行時間,也就是通常所說的減小鎖的粒度。

  

synchronized可以使用任意的Object進行加鎖,用法比較靈活。

  

另外特別注意一個問題,就是不要使用String的常量加鎖,會出現死循環問題。

  

鎖對象的改變問題,當使用一個對象進行加鎖的時候,要注意對象本身發生改變的時候,那麼持有的鎖就不同。如果對象本身不發生改變,那麼依然是同步的,即使是對象的屬性發生了改變。

  

死鎖問題 在設計程序時就應該避免雙方相互持有對方的鎖的情況,死鎖後,線程會一直嘗試獲得鎖,不會終止程序

  

示例:ObjectLock Optimize StringLock ModifyLockDeadLockChangeLock

  

1.7 volatile關鍵字

volatile概念:volatile關鍵字的主要作用是使變量在多個線程間可見。

示例總結:

在java中,每一個線程都會有一塊工作內存區,其中存放着所有線程共享的主內存中的變量

值的拷貝。當線程執行時,他在自己的工作內存區中燥作這些變量。爲了存取一個共享的變量,

一個線程通常先獲取鎖定並去清除它的內存工作區,把這些共享變量從所有線程的共享內存區中

正確的裝入到他自己所在的工作內存區中,當線程解鎖時保證該工作內存區中變量的值寫回到共

享內存中。

一個線程可以執行的操作有使用(use)、賦值(assign)、裝載(load)、存儲(store)、鎖定(lock)、解鎖(unlock)。

而主內存可以執行的操作有讀(read)、寫(write)、鎖定(lock)、解鎖(unlock),每個操作都是原子的。

volatile的作用就是強制線程到主內存(共享內存)裏去讀取受量,而不去線程工作內存區

裏去讀取,從而實現了多個線程間的變量可見。也就是滿足線程安全的可見性。

  

示例:RunThread

  

1.7 volatile 關鍵字(線程執行流程圖)

 

  

1.8 volatile關鍵字的非原子性

Volatile 關鍵字雖然擁有多個線程之間的可見性,但是卻不具備同步性(也就是原子性),可以算上是一個輕量級的synchronized,性能要比synchronized 強很多,不會造成阻塞(在很多開源的架構裏,比如 netty 的底層代碼就大量使用volatile,可見 netty 性能一定是非常不錯的。)這裏需要注意:一般 volatile 用於只針對於多個線程可見的變量操作,並不能代替 synchronized 的同步功能。

示例總結:

volatile 關鍵字只具有可見性,沒有原子性。要實現原子性建議使用 atomic 類的系列對象,支持原子性操作(注意atomic 類只保證本身方法原子性,並不保證多次操作的原子性)

  

示例: VolatileNoAtomicAtomicUse

發佈了58 篇原創文章 · 獲贊 16 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章