<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # 什么是鎖? 如果從日常生活中理解什么是鎖,很好理解,每個人家門上都有鎖,用來防止他人竊取自家財產。但是在計算機中,鎖的概念稍有不同,在計算機中只有涉及到資源競爭的時候,才會用到鎖。 比如在單線程中,不需要用到鎖,資源都是順序化被持有,不存在競爭。但是在多線程中,同時會有多個請求需要同一個資源,這個時候,就需要進行加鎖操作,一個線程獲取到鎖之后,其他的線程只有等待資源被釋放才能接著執行。 # 鎖的作用 鎖本質是為了保證串行,比如在購買訂單的時候,同時涌入大量的請求,如何保證商品不多賣以及不少賣,保證數據的準確性,這個時候就要用鎖來控制并發,讓本來并行執行的問題轉換為串行執行。 # 鎖的分類 說到鎖的分類,在各種文章以及研究中都提供了不同的分類,比較繁雜,但是如果從思想上來說,總體來說分為兩類,一類是悲觀鎖,一類是樂觀鎖。 # 樂觀鎖 樂觀鎖是相對于悲觀鎖而言,樂觀鎖機制采用了更加寬松的加鎖機制。樂觀鎖字如其名,就是持有比較樂觀的態度。就是假設認為數據一般情況下不會造成沖突,所以在數據進行提交更新的時候,才會正式對數據的沖突與否進行檢測,如果發現沖突了,則給用戶返回錯誤信息,讓用戶決定如何去做。 樂觀鎖的實現也比較簡單,就是使用數據版本version記錄機制實現。接下來,我們利用mysql數據庫來實現一下樂觀鎖,但是這里要特別注意,第一點,mysql本身并沒有提供實現樂觀鎖,而且也沒有樂觀鎖這個概念,mysql的鎖都是悲觀鎖,那么我們就懂了,樂觀鎖是一種思想,使用其他的所有方式都可以實現,只要實現了這個思想的,都叫樂觀鎖。比如可以使用文件,使用redis都可以實現。 我們首先建立一張表叫good\_num貨物表。 創建表的語句如下所示 ~~~ CREATE TABLE `good_nums` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) DEFAULT NULL, `nums` int(11) DEFAULT NULL, `version` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ~~~ | 字段 | 描述 | | --- | --- | | id | 自增ID | | name | 名字 | | nums | 數量 | | version | 版本 | 接下來我們給表增加一條記錄。 ~~~ INSERT INTO good_amount (name,nums,version) VALUES('張三',100,1) ~~~ 目前記錄如下所示: | id | name | nums | version | | --- | --- | --- | --- | | 1 | 張三 | 100 | 1 | OK,基本情況我們已經構建完成。 那么現在我們假定有這么一個需求,用戶每買一筆,就要把數量減少1個。理想情況下,如果用戶一個一個來去購買的話,我們的數量會按照情況去一個一個減少。但是這僅僅是理想情況,實際情況是,用戶會同時涌入來購買,在這種情況下,數量的減少根本無法保障。 我們看一下在未使用鎖的情況下,是如何實現程序的,這也是初級程序員最常寫的代碼如下所示: ~~~ <?php //連接數據庫 $conn = new mysqli_connect(xxxxx); //查詢當前的數量 $sql = "SELECT * from good_nums where id =1"; $source = $conn->query($sql); $row = $source->fetch_assoc(); if($row['nums'] <= 0){ echo "很遺憾,商品被搶光了"; exit; } //將當前的數量減少1 $nums = $row['nums'] - 1; $sql1 = "update good_nums set nums=".$nums." where id=1"; $source1 = $conn->query($sql1); ~~~ 通過上面的代碼可以很明顯的理解,當大量的請求同時涌來的時候,程序在同一時間可能同時讀到當前數量為100,那么每個用戶可能都搶到了物品,但是數據庫記錄數到最后居然是99,然而此時可能已經賣了1千個了。此時就發生了超賣的問題。 那么如果此時使用樂觀鎖,就很容解決這個問題了。代碼如下所示: ~~~ // 連接數據庫 $conn = new mysqli(xxxx); //查詢當前的數量 $sql = "SELECT * from good_nums where id =1"; $source = $conn->query($sql); $row = $source->fetch_assoc(); if($row['nums'] <= 0){ echo "很遺憾,商品被搶光了"; exit; } //給當前的數量減少1 $nums = $row['nums'] - 1; //當前的版本 $version = $row['version']; //更新數據庫的數量 $sql1 = "update good_nums set nums=".$nums.",version=version+1 where id=1 and version=".$version; $source1 = $conn->query($sql1); if(!$source1){ echo "您未搶到本商品,請繼續努力"; } $conn->close(); ~~~ 可以對比現在的程序,比之前的程序多增加了一個version的條件控制,對的,這就是樂觀鎖的精髓所在。 我們現在來模擬一下用戶的請求。 當第一個用戶涌進來,他拿到的貨物數量為100,此時拿到的version為1。 當第二個用戶涌進來,他此時拿到的貨物數量也為100,此時的version為1。 還有第三個,第四個,都是同樣的情況。 此時,第一個用戶的請求去更新數據庫,他更新的條件,是version必須等于剛剛拿到1,此時數據庫的version還未更新,于是他將這條記錄更新成功,nums數量成功減少為99,并且于此同時,將version更新為2。 那么第二個用戶也去執行這個條件,數據庫此時去檢查發現version居然不是1了,于是這個條件不成立,此時這條sql就會執行失敗,然后告訴用戶你沒有搶到。 一直第三個,第四個用戶都是如此。 只有第n個用戶可能拿到新的version,并且可能成功更新。 上面就是樂觀鎖的完整實現,當前了,上面我們寫的程序還有問題,下面是一個正確的程序。 ~~~ // 連接數據庫 $conn = new mysqli(xxxx); //查詢當前的數量 $sql = "SELECT * from good_nums where id =1"; $source = $conn->query($sql); $row = $source->fetch_assoc(); if($row['nums'] <= 0){ echo "很遺憾,商品被搶光了"; exit; } //當前的版本 $version = $row['version']; //更新數據庫的數量 $sql1 = "update good_nums set nums=nums-1,version=version+1 where id=1 and version=".$version; $source1 = $conn->query($sql1); if(!$source1){ echo "您未搶到本商品,請繼續努力"; } $conn->close(); ~~~ 仔細對比就會發現,之前我們是用程序去減少的數量1,但是現在的代碼是讓數據庫去自動減少1。這個也是比較關鍵的一點,在高并發的情況下,請記住一定要如此寫,不然可能會發生不可想像的錯誤。 在很多時候,如果面試官繼續深究,他就會問你,如果大量的用戶同時涌入,上面的程序只能保證少數人能拿到商品啊,可能100個用戶同時涌入,到最后,只賣了10個,但是公司又想這100都賣掉,這個時候咋辦? 這個時候我們就引入了自旋鎖的概念。這個概念如果第一次聽說,就會有點蒙,啥是自旋,怎么自旋,好,帶著這個問題,我們回到剛剛100個人來搶,可能只賣了10個問題。 我們想,既然用戶會發生更新失敗的問題,我們為啥不如讓用戶等待一下,重新獲取一遍新的值,然后讓用戶搶到呢?對的,這就是自旋鎖了。 程序如下: ~~~ // 連接數據庫 $conn = new mysqli(xxxx); $tips = true; while($tips){ //查詢當前的數量 $sql = "SELECT * from good_nums where id =1"; $source = $conn->query($sql); $row = $source->fetch_assoc(); if($row['nums'] <= 0){ echo "很遺憾,商品被搶光了"; $tips = false; exit; } //當前的版本 $version = $row['version']; //更新數據庫的數量 $sql1 = "update good_nums set nums=nums-1,version=version+1 where id=1 and version=".$version; $source1 = $conn->query($sql1); //當數量更新失敗了 if($source1 == true){ $tips = fasle; } } echo "恭喜您搶到了商品"; $conn->close(); ~~~ 我們看到,首先我們會給一個標識位為true,就是只有不滿足條件的時候才退出,也就是只有用戶搶到了,才結束掉本次請求。如果沒有搶到,程序會不斷的請求,讓當前的用戶去搶,這就是自旋鎖,實際上,就是用了一個循環,不端的去請求,當然了,也可以用遞歸,但是本質都是相同的,都是循環。 但是上面的程序有個問題,如果用戶一直沒有搶到,程序就會一直執行,如果請求數量巨大,就會發生大量的timeout,我們能不能像個辦法,提前結束掉循環呢?程序如下: ~~~ // 連接數據庫 $conn = new mysqli(xxxx); $tips = true; $count = 5; while($tips){ //查詢當前的數量 $sql = "SELECT * from good_nums where id =1"; $source = $conn->query($sql); $row = $source->fetch_assoc(); if($row['nums'] <= 0){ echo "很遺憾,商品被搶光了"; $tips = false; exit; } //當前的版本 $version = $row['version']; //更新數據庫的數量 $sql1 = "update good_nums set nums=nums-1,version=version+1 where id=1 and version=".$version; $source1 = $conn->query($sql1); //當數量更新失敗了 if($source1 == true){ $tips = fasle; } $count--; if($count <= 0){ $tips = false; } } echo "恭喜您搶到了商品"; $conn->close(); ~~~ 上面的程序,我們給程序增加了一個count值,當程序執行超過了限定,我們就會釋放掉本次循環,需要說明的是,這不是自旋鎖的概念,只是為了優化請求,才這么寫的,為的是保證程序不超時。 自旋鎖如果按照簡拼來說,通常被叫做CAS。面試的時候,如果被問題到CAS是什么,一定要知道,指代的是自旋鎖。 但是上面的自旋鎖還是有缺點的,就是在未加count的控制的時候,程序會不斷地循環,會給CPU造成多余的計算能力,為了解決這個問題,對于自旋鎖又提出了其他的實現方法,如果有興趣可以自行百度。一般來說,自旋鎖的概念多在Java 面試中提及,因為Java本身支持鎖操作,也支持多線程,可以利用多線程對自旋鎖進行優化,但是本質都是一樣的,即循環搶占鎖。 # 悲觀鎖 悲觀鎖,顧名思義,就是很悲觀,在每次操作的時候,都認為別人已經進行了修改,所以,每次去拿數據的時候都會先進行加鎖操作,防止其他人搶占資源。 接下來,將使用給文件加鎖的形式實現悲觀鎖。代碼如下: ~~~ <?php $file = "/home/work/abc.txt"; //給文件加鎖 if(flock($file,LOCK_EX|LOCK_NB)){ //這里表示搶占到了鎖,可以執行業務邏輯了 //todo ..... //執行完成之后,記得要釋放掉鎖 flock($file, LOCK_UN); }else{ echo "很不好意思,您沒有搶到鎖"; } ~~~ 通過上面的代碼,我們就很容易理解悲觀鎖,當請求過來的時候,我們先將文件鎖定,搶占到這個資源,如果于此同時再有其他到請求過來,他們沒有搶占到鎖,其他的任務都會失敗。只有等待搶占鎖的任務成功釋放掉鎖之后,其他的任務才可以繼續搶占鎖從而繼續任務。 當然了,我這里寫的代碼是直接告訴用戶 # 樂觀鎖和悲觀鎖的對比 通過上面樂觀鎖和悲觀鎖的例子,我們就很容易理解了。 樂觀鎖保持樂觀的態度,不會一進來就認為別人動了自己的資源,只有更新的時候,才會檢查一下,如果此時發現被別人更改過了,那么就直接返回失敗,樂觀鎖適用于讀多寫少的場景,我們想,如果一個任務需要大量的更新,使用了樂觀鎖,那么大部分任務不都將失敗了嗎?所以要考慮好場景再使用。 悲觀鎖是保持悲觀的態度,一進來就先將資源占有起來,只有自己的任務全部完成之后,才釋放資源。 當然了,最后一點,最重要的是理解悲觀鎖和樂觀鎖是一種思想,也就是不局限于任何形式的實現。 原文:http://www.hmoore.net/missyou/interview/2234864
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看