以下都是用自己的語言的自我理解與總結,僅供參考
首先,要明白為什么會有鎖,簡單點就是,多個進程(或者多個線程)需要同時修改同一個資源的時候,為了保證順序修改而加的鎖,如果不順序修改,那就會錯亂。
1、線程和進程的區別
進程:系統資源分配的最小單位,指運行中的應用程序。
線程:系統分配處理器時間資源的基本單元,運行在進程中的一個單元執行流,一個進程可以有多個線程。
一個進程中的多個線程之間內存數據是共享的,共享的數據修改需要加鎖,在java中就可以用synchronized或者reentrantlock實現
多個進程之間的內存是獨立的,不共享的,多個進程之間的通信常用的有管道(如linux中的“|”),socket請求(如http請求數據)
2、java中的鎖
主要有synchronized 和 reentrantlock 2種方法實現
reentrantlock支持鎖等待,鎖返回(即tryLock()如果加鎖不成功返回false,成功返回true),鎖等待超時返回(tryLock(time),等待time后還獲取不到鎖就返回false)
synchronized只支持鎖等待
相比reentrantlock適用的場景更多
3、樂觀鎖、悲觀鎖、死鎖
a、樂觀鎖:查詢數據的時候不加鎖,修改數據的時候檢查數據是否有沖突,如果有沖突,就返回錯誤,讓用戶重試或者其他操作,一般通過加版本號,或者時間戳實現,實際並沒有加鎖的操作,不會發生死鎖
b、悲觀鎖:在查詢數據的時候就加鎖,然后再執行真正的修改數據操作,修改完成,再釋放鎖。優點是安全,缺點是,減少了系統的並行性。主要由數據庫自主實現悲觀鎖,比如mysql的 select ... for update
c、死鎖:四個必要條件,1、互斥,2、不可剝奪,3、請求與保持,4、循環等待,由於悲觀鎖用到了鎖,實際只有悲觀鎖時才會發生死鎖,避免死鎖,就是打破四個條件之一,簡單的可以打破第四個條件,不讓循環等待,設置超時機制,如果等待固定時間還獲取不到鎖,就拋出錯誤,釋放自己的鎖
d、mysql模擬死鎖,
比如2個事務,事務A、事務B,都查詢id=1和id=2的數據,並加鎖
事務A,select * from t where id=1 for update,對id=1數據加鎖
然后,事務B,select * from t where id=2 for update,對id=2數據加鎖
事務A,此時又需要查詢id=2的數據,於是select * from t where id=2 for update,因為事務B已經加過id=2的鎖了,所以事務A就會一直等待
事務B,此時又需要查詢id=1的數據,於是select * from t where id=1 for update,因為事務A已經加過id=1的鎖了,所以事務B也會一直等待
此時就發生了死鎖,會等待系統設置的超時時間,mysql會檢查這種死鎖,超過超時時間(mysql設置innodb_lock_wait_timeout),然后報錯回滾,才打破死鎖
4、mysql中的鎖
mysql innodb 模式中有表級索、行級鎖
行級鎖又有排他鎖、共享鎖,查詢加排他鎖就是select ... for update這種,正常的update delet insert語句會自動加上排他鎖
共享鎖,在查詢中加上lock in share mode就表示共享鎖,適用於兩張表存在業務關系時的一致性要求,for update適用於操作同一張表時的一致性要求,參考大佬博客,https://blog.csdn.net/cug_jiang126com/article/details/50544728
表級索,mysql5.7之前的ddl操作會鎖表,比如加字段,加索引,之后不會,還有ddl中,如果update 是全表的數據也會鎖表