傳統的解決方法很簡單。
* 如果是單個資料的話,對資料使用volatile
關鍵字修飾,這個關鍵字可以保證資料不會停留在cpu的寫快取中,而是寫入記憶體,下一次對該資料的讀取一定讀到的記憶體中的最新資料。也就是解決了可見性問題。但是volatile變數原子性還有問題,對volatile變數多執行緒下的自增自減操作,不是原子的。
* 如果是集合類,傳統的做法是交給collections.synchronizated*
方法,得到乙個同步的集合,但是這個集合的效率並不會很高。
* 如果是**塊或者資料結構,傳統的解決方法是使用關鍵字synchronizated
,這個關鍵字可以修飾類、方法、變數甚至**段。這個關鍵字會告知jvm,當前執行緒得到了某乙個物件(根據修飾的內容不同,物件不同)的鎖,對當前**的訪問就和單執行緒下一樣了。這個關鍵字對可見性和原子性都解決了。但是這種機制得到的鎖是「重量鎖」,也就是獲取鎖和釋放鎖都比較浪費時間,如果需要同步加鎖的**太多,這種方法效率較低。
* 如果需要多執行緒間通訊同步,可以使用定義在object
上的wait
和notify*
方法,可以使執行緒在呼叫wait方法時進入等待狀態,在呼叫notify*
方法時被喚醒。
jdk1.5之後引入了很多併發的類,放到並發包下,再加上對原有機制的優化,併發的效率更高了。
* 如果是單個資料,依舊使用volatile
關鍵字,在jdk5後,該關鍵字的能力得到了增強,有了獲取鎖釋放鎖的語義,也就是說如果是對單個資料加鎖,完全可以用volatile
替代。但是該關鍵字還是自增自減操作不是原子的。
* 對於需要原子操作的單個資料,並發包下有atomic*
的一系列類,實現了原子地修改、自增、自減乙個變數。
* 如果是集合類,並發包下都有併發容器可供直接使用,就和使用一般的集合類一樣,當然內部已經實現了同步,效率更高。
* 如果是**塊或者資料結構,可以使用並發包下的lock
的一系列實現類(目前只有可重入鎖和讀寫鎖),替代synchronizated
關鍵字,優點是效率更高,缺點是需要顯式地獲取和釋放鎖。
* 如果需要執行緒間通訊同步,可以使用並發包下的condition
介面,相當於訊號量,可以實現在特定情況下等待或者繼續執行,與object
的wait
和notify
操作相同。
* 此外,jdk1.5後還提供併發工具類,方便應對各種兵法情況。當然,這些工具類的實現基礎,還是上面提到的volatile
、lock
和condition
。
Java併發程式設計的藝術 筆記
併發存在的問題 上下文切換耗時,死鎖,軟硬體資源限制 解決方法 減少上下文切換 1.無鎖併發程式設計 讓不同的執行緒處理不同的資料段 將資料id採用hash演算法分配給不同的執行緒 2.cas演算法 compare and set使用jni 3.使用最少執行緒 減少處於waiting狀態的執行緒 j...
java併發程式設計的藝術(六) AQS
aqs是佇列同步器 abstractqueuesynchronizer 是用來構建鎖和完成其他同步元件的基本框架,再lock裡面,很多的方法都將用到aqs以獲取同步狀態,實現鎖的語義,而不是依靠傳統的synchronized中的物件進行鎖獲取與釋放。aqs的核心思想是基於volatile int s...
Java併發程式設計的藝術 筆記2
現代作業系統排程的最小單位是執行緒,也叫輕量級程序,每個程序可建立多個程序,每個執行緒都有各自的計數器 堆疊和區域性變數等屬性,並且能夠訪問共享變數。處理器在這些執行緒上高速切換,讓使用者感覺到這些執行緒在同時執行。執行緒優先順序 決定執行緒分配處理器資源多少的屬性,設定時,針對頻繁阻塞 休眠或i ...