1、鎖機制java
經常使用的鎖機制有兩種:算法
一、悲觀鎖:假定會發生併發衝突,屏蔽一切可能違反數據完整性的操做。悲觀鎖的實現,每每依靠底層提供的鎖機制;悲觀鎖會致使其它全部須要鎖的線程掛起,等待持有鎖的線程釋放鎖。數據庫
二、樂觀鎖:假設不會發生併發衝突,每次不加鎖而是假設沒有衝突而去完成某項操做,只在提交操做時檢查是否違反數據完整性。若是由於衝突失敗就重試,直到成功爲止。樂觀鎖大可能是基於數據版本記錄機制實現。爲數據增長一個版本標識,好比在基於數據庫表的版本解決方案中,通常是經過爲數據庫表增長一個 「version」 字段來實現。讀取出數據時,將此版本號一同讀出,以後更新時,對此版本號加一。此時,將提交數據的版本數據與數據庫表對應記錄的當前版本信息進行比對,若是提交的數據版本號大於數據庫表當前版本號,則予以更新,不然認爲是過時數據。 多線程
樂觀鎖的缺點是不能解決髒讀的問題。併發
在實際生產環境裏邊,若是併發量不大且不容許髒讀,可使用悲觀鎖解決併發問題;但若是系統的併發很是大的話,悲觀鎖定會帶來很是大的性能問題,因此咱們就要選擇樂觀鎖定的方法.
鎖機制存在如下問題:
(1)在多線程競爭下,加鎖、釋放鎖會致使比較多的上下文切換和調度延時,引發性能問題。
(2)一個線程持有鎖會致使其它全部須要此鎖的線程掛起。
(3)若是一個優先級高的線程等待一個優先級低的線程釋放鎖會致使優先級倒置,引發性能風險。
性能
2、CAS 操做this
JDK 5以前Java語言是靠synchronized關鍵字保證同步的,這是一種獨佔鎖,也是是悲觀鎖。java.util.concurrent(J.U.C)種提供的atomic包中的類,使用的是樂觀鎖,用到的機制就是CAS,CAS(Compare and Swap)有3個操做數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改成B,不然什麼都不作。atom
現代的CPU提供了特殊的指令,容許算法執行讀-修改-寫操做,而無需懼怕其餘線程同時修改變量,由於若是其餘線程修改變量,那麼CAS會檢測它(並失敗),算法能夠對該操做從新計算。而 compareAndSet() 就用這些代替了鎖定。spa
以AtomicInteger爲例,研究在沒有鎖的狀況下是如何作到數據正確性的。.net
字段value須要藉助volatile原語,保證線程間的數據是可見的(共享的)。這樣在獲取變量的值的時候才能直接讀取。而後來看看++i是怎麼作到的。getAndIncrement採用了CAS操做,每次從內存中讀取數據而後將此數據和+1後的結果進行CAS操做,若是成功就返回結果,不然重試直到成功爲止。而compareAndSet利用JNI來完成CPU指令的操做。
總體的過程就是這樣子的,利用CPU的CAS指令,同時藉助JNI來完成Java的非阻塞算法。其它原子操做都是利用相似的特性完成的。
而整個J.U.C都是創建在CAS之上的,所以對於synchronized阻塞算法,J.U.C在性能上有了很大的提高,是非阻塞的。
轉載:https://blog.csdn.net/heyutao007/article/details/19975665