聊聊無鎖編程設計
本文轉載自微信公眾號「Code視角」,作者Code視角。轉載本文請聯系Code視角公眾號。
什么是無鎖編程
LOCK-FREE,字面解釋就是不通過鎖來解決多線程、多進程之間的數據同步和訪問的程序設計方案。相對來說就是通過數據結構和算法來解決數據并發(fā)沖突的實現方案。
無鎖編程的實現
「比較并交換 Compare-and-swap」
compare and swap,解決多線程并行情況下使用鎖造成性能損耗的一種機制,CAS操作包含三個操作數——內存位置(V)、預期原值(A)和新值(B)。如果內存位置的值與預期原值相匹配,那么處理器會自動將該位置值更新為新值。否則,處理器不做任何操作。無論哪種情況,它都會在CAS指令之前返回該位置的值。CAS有效地說明了“我認為位置V應該包含值A;如果包含該值,則將B放到這個位置;否則,不要更改該位置,只告訴我這個位置現在的值即可。(百度百科)
參考圖
使用場景
(1) 樂觀鎖的實現方案:不加鎖,假設沒有沖突去完成某項操作,如果因為沖突失敗就重試,直到成功為止。
缺點
(1)循環(huán)開銷問題。長時間更改不成功,會來帶大量的CPU消耗。解決方法:需要在修改失敗后執(zhí)行其它邏輯, 且CAS并不適合資源大量競爭的情況。
(2)ABA問題:線程1準備用CAS將變量的值由A替換為B,在此之前,線程2將變量的值由A替換為C,又由C替換為A,然后線程1執(zhí)行CAS時發(fā)現變量的值仍然為A,所以CAS成功。但實際上這時的現場已經和最初不同了。
「數據Hash」
數據Hash其實就是通過Hash算法把數據提前來確定由哪個節(jié)點進行處理或者存儲,解決數據并發(fā)的思想是通過算法解決不同的數據到不同的節(jié)點。算法:數據.hashCode() % 節(jié)點數量。
參考圖
使用場景
(1)定時任務處理數據時。例如:一個定時任務數據量較多,需要集群處理。那么就可以同時啟動任務讀取數據,然后根據idHash來決定當前節(jié)點是否要處理這條數據。
(2)請求到指定服務器進行處理。例如:Nginx ipHash轉發(fā)策略,Kafka hash分區(qū)保證分區(qū)有序性。
缺點
(1) 擴容相對復雜,需要進行數據遷移。例如一致性hash算法,Kafka分區(qū)再均衡策略。但是某些場景不一定支持擴容。
(2) hash算法是否散列,如果算法不夠散列會出現數據傾斜問題。
「單線程」
某些場景下單線程的設計要比多線程更加優(yōu)秀, 單線程下不存在資源競爭、線程切換,當然也取決于你當前的服務器配置。
例如:
(1)Redis的設計上,由于內存級別的K/V數據庫,在處理核心讀寫時如果頻繁的CPU切換、線程等待喚醒和鎖資源獲取,反倒會造成性能瓶頸。
(2)在生成分布式id的場景下, 某臺id服務器批量生成id 這個時候也可以進行單線程處理,內存計算非常高效。
什么時候使用單線程?
(1)單核服務器。
(2)業(yè)務場景大量CPU計算且數據沖突較多的情況下(非絕對)。
無鎖編程的優(yōu)缺點
「優(yōu)點」
不會有優(yōu)先級倒置。
不會出現死鎖、饑餓、餓死等問題。
減少資源競爭,CPU資源消耗少,更高效。
「缺點」
具有一定的復雜性,需要一定的算法思想。
不適合所以的場景,非全局最優(yōu)解。
總結
在設計程序時, 應該考慮程序的使用場景來進行最優(yōu)的數據結構和算法來進行方案設計。無鎖編程也只是解決某些場景的一種方案,并不一定代表著最優(yōu)解。
結語
優(yōu)秀的設計模式結合優(yōu)秀的數據結構相才能帶來優(yōu)秀的代碼。編程人的內功心法:數據結構+算法。