爲什麼分佈式環境下synchronized失效?如何解決這種情況?

synchronized關鍵字失效原因

在Java多線程編程中,經常會用到synchronized和lock和原子變量等,而在分佈式系統中,由於分佈式系統中的分佈性,即多線程和多進程併發 分佈在不同機器中,synchronized和lock這兩種鎖將失去原有鎖的效果,因此需要自己實現分佈式鎖來處理併發問題,分佈式處理併發的辦法有以下三種:

隊列

定義:將所有要執行的任務放入隊列中,然後一個一個消費,從而避免併發問題

悲觀鎖

將數據記錄加版本號,如果版本號不一致就不更新,這種方式同Java的CAS理念類似。

分佈式鎖

常見的分佈式鎖有以下三種實現:

基於數據庫實現的分佈式鎖

利用數據庫表:首先創建一張鎖的表主要包含下列字段:方法名、時間戳等。——方法名稱要有唯一性約束

  • 1、如果有多個請求同時提交到數據庫時,數據庫保證只有一個操作可以成功,那麼操作成功的那個線程獲得了該方法的鎖,可以繼續執行下面的方法體內容。
    優化: 記錄當前獲得該鎖的機器的主機信息和線程信息,那麼下次再次獲取鎖的時候就可以先查詢數據庫,如果當前機器的主機信息和線程信息可以在數據庫中查到,那麼就可以直接把鎖分配給它,從而實現可重入鎖。
  • 2、基於數據庫排他鎖
    在查詢語句後面增加for update,數據庫會在查詢過程中給數據庫表增加排他鎖,當某條記錄被加上排他鎖之後,其他線程無法在該行記錄增加排他鎖。其他沒有獲取到的鎖就會阻塞在select語句上,從而有兩種可能的結果:在超時之前獲取到了鎖和在超時之前沒有獲取到鎖。獲得排他鎖的線程即可獲取分佈式鎖,當獲取到鎖之後,可以執行方法的業務邏輯,執行完方法之後,釋放鎖connection.commit()。存在的問題主要是性能不高和sql超時的異常。

基於Zookeeper實現分佈式鎖

基於Zookeeper臨時有序節點可以實現的分佈式鎖。每個客戶端對某個方法加鎖時,在Zookeeper上與該方法對應的指定節點的目錄下,生成一個唯一的瞬時有序節點。

  • 1、判斷是否獲取鎖的方式只需要判斷有序節點中的序號最小的一個。當釋放鎖的時候,只需將這個瞬時節點刪除即可。同時,其可以避免服務宕機導致的鎖無法釋放,而產生的死鎖問題。
  • 2、提供的第三方庫有curator,Curator提供的InterProcessMutex是分佈式鎖的實現。acquire方法獲取鎖,release方法獲取鎖。

基於緩存(redis)來實現分佈式鎖

採用jedis.setnx()和jedis.expire()組合實現加鎖。setnx()方法作用就是SET IF NOT EXIST,expire方法就是給鎖加一個過期時間。由於這是兩條Redis命令,不具有原子性,如果程序在執行完setnx()之後突然崩潰,導致鎖沒有設置過期時間。那麼就會發生死鎖。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章