Java業務原子性的一種實現(key 獨佔訪問)

來源:互聯網
上載者:User

標籤:

開發過程中,有時候為瞭解決多線程競爭問題需要加鎖,通常鎖定的對象是class,object,method,但在特定時候我們需要更細粒度的加鎖,也就是根據不同輸入參數來鎖定不同的資源,這樣只有調用此方法的不同線程傳參一樣才會進行競爭。

比如一個簡單的例子:假設系統為使用者提供借款,每月有個限額。每月的借款記錄都記在transaction_detail表中,此時當一個使用者需要進行借款的時候,我們需要進行一下操作:

method:borrow_money

1、判斷使用者是否達到借款限額,本月已借款量:select sum(amount) from transaction_detail where user_id=XX;

2、其他業務處理

3、插入transaction_detail

但是以上三步並非原子的,也就是假設使用者限額為3w,已借款量1.5w(還可借1.5w),現在有兩個線程a、b,每個線程都需要借款1w。

當線程a執行完第一步判斷,此時線程a掛起。

線程2開始執行,由於此時借款1w<可借的1.5w,於是線程2執行下去成功貸款1w,插如新的貸款記錄。

a線程此時繼續執行2、3步也成功貸款了1w,這樣該使用者當月貸款量已達3.5w大於總限額了(違背了每月3w的限額)。導致這個情況就是以上3步非原子的,在我們執行借款、插入借款記錄的貸款狀態很可能與我們判斷當月已貸款量已經不同了。

這樣我們就需要對上面的三步進行加鎖同步,但是如果我們將上訴方法borrow_money使用synchronized,將會引發很嚴重的效率問題,因為這樣整個系統的所有線程在執行borrow_money都是競爭關係的,這樣就會造成系統效能瓶頸。

於是我們可以使用一種更細粒度的加鎖機制。可以對特定字串加鎖來保證操作的原子性同時減少線程對資源的競爭,這個特定字串需要與使用者一對一的關聯,同時確保不會影響系統其它操作,所以可以使用:特殊字元串(確保字串具有特殊性)  + id。

而且我們的字串需要到jvm的常量池中擷取這樣確保對於相同的使用者擷取的字串是一個對象。

String syncKey = ("borrow_money_" + userId).intern();synchronized (syncKey) {    //1、判斷使用者本月已借款量:select sum(amount) from transaction_detail where user_id=XX;  //2、其他業務處理  //3、插入transaction_detail}

如上便解決對於同一個key 獨佔訪問,但這僅限於同一個jvm,如果伺服器部署在叢集上便無法達到預期效果。

此時可以將每月總借款資訊記錄在一張表中,這樣在判斷與操作的時候可以根據這條記錄來判斷狀態

或者使用分布式的鎖,如:ZooKeeper進行管理。

 

Java業務原子性的一種實現(key 獨佔訪問)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.