什麼場景下須要使用鎖?
在多節點部署或者多線程執行時,同一個時間可能有多個線程更新相同數據,產生衝突,這就是併發問題。這樣的狀況下會出現如下問題:
更新丟失:一個事務更新數據後,被另外一個更新數據的事務覆蓋。
髒讀:一個事務讀取另外一個事物爲提交的數據,即爲髒讀。
其次還有幻讀。。
針對併發引入併發控制機制,即加鎖。
加鎖的目的是在同一個時間只有一個事務在更新數據,經過鎖獨佔數據的修改權。
鎖的實現方式
一、悲觀鎖,前提是,必定會有併發搶佔資源,強行獨佔資源,在整個數據處理過程當中,將數據處於鎖定狀態。
二、樂觀鎖,前提是,不會發生併發搶佔資源,只有在提交操做的時候檢查是否違反數據完整性。只能防止髒讀後數據的提交,不能解決髒讀。
固然,還有其餘的鎖機制,暫時很少介紹,着重於樂觀鎖的實現。
樂觀鎖,使用版本標識來肯定讀到的數據與提交時的數據是否一致。提交後修改版本標識,不一致時能夠採起丟棄和再次嘗試的策略。
記錄1,id,status1,status2,stauts3,version,表示有三個不一樣的狀態,以及數據當前的版本
操做1:update table set status1=1,status2=0,status3=0 where id=111;
操做2:update table set status1=0,status2=1,status3=0 where id=111;
操做3:update table set status1=0,status2=0,status3=1 where id=111;
沒有任何控制的狀況下,順序執行3個操做,最後前兩個操做會被直接覆蓋。
加上version字段,每一次的操做都會更新version,提交時若是version不匹配,中止本次提交,能夠嘗試下一次的提交,以保證拿到的是操做1提交後的結果。
這是一種經典的樂觀鎖實現。
另外,java中的compareandswap即cas,解決多線程並行狀況下使用鎖形成性能損耗的一種機制。
CAS操做包含三個操做數,內存位置(V),預期原值(A)和新值(B)。若是內存位置的值與預期原值相匹配,那麼處理器會西東將該位置值更新爲新值。不然,處理器不作任何操做。
記錄2: id,stauts,status 包含3種狀態值 1,2,3
操做,update status=3 where id=111 and status=1;
即 若是內存值爲1,預期值爲1,則修改新值。對於沒有執行的操做則丟棄。
思考:這兩種方式有什麼區別?