什么是樂(lè)觀鎖,什么是悲觀鎖

一、并發(fā)控制

當(dāng)程序中可能出現(xiàn)并發(fā)的情況時(shí),就需要保證在并發(fā)情況下數(shù)據(jù)的準(zhǔn)確性,以此確保當(dāng)前用戶和其他用戶一起操作時(shí),所得到的結(jié)果和他單獨(dú)操作時(shí)的結(jié)果是一樣的。這就叫做并發(fā)控制。并發(fā)控制的目的是保證一個(gè)用戶的工作不會(huì)對(duì)另一個(gè)用戶的工作產(chǎn)生不合理的影響。

沒(méi)有做好并發(fā)控制,就可能導(dǎo)致臟讀、幻讀和不可重復(fù)讀等問(wèn)題。

常說(shuō)的并發(fā)控制,一般都和數(shù)據(jù)庫(kù)管理系統(tǒng)(DBMS)有關(guān)。在 DBMS 中并發(fā)控制的任務(wù),是確保多個(gè)事務(wù)同時(shí)增刪改查同一數(shù)據(jù)時(shí),不破壞事務(wù)的隔離性、一致性和數(shù)據(jù)庫(kù)的統(tǒng)一性。

實(shí)現(xiàn)并發(fā)控制的主要手段分為樂(lè)觀并發(fā)控制和悲觀并發(fā)控制兩種。
無(wú)論是悲觀鎖還是樂(lè)觀鎖,都是人們定義出來(lái)的概念,可以認(rèn)為是一種思想。其實(shí)不僅僅是關(guān)系型數(shù)據(jù)庫(kù)系統(tǒng)中有樂(lè)觀鎖和悲觀鎖的概念,像 hibernate、tair、memcache 等都有類似的概念。所以,不應(yīng)該拿樂(lè)觀鎖、悲觀鎖和其他的數(shù)據(jù)庫(kù)鎖等進(jìn)行對(duì)比。樂(lè)觀鎖比較適用于讀多寫(xiě)少的情況(多讀場(chǎng)景),悲觀鎖比較適用于寫(xiě)多讀少的情況(多寫(xiě)場(chǎng)景)。

二、悲觀鎖(Pessimistic Lock)

1??理解
當(dāng)要對(duì)數(shù)據(jù)庫(kù)中的一條數(shù)據(jù)進(jìn)行修改的時(shí)候,為了避免同時(shí)被其他人修改,最好的辦法就是直接對(duì)該數(shù)據(jù)進(jìn)行加鎖以防止并發(fā)。這種借助數(shù)據(jù)庫(kù)鎖機(jī)制,在修改數(shù)據(jù)之前先鎖定,再修改的方式被稱之為悲觀并發(fā)控制【Pessimistic Concurrency Control,縮寫(xiě)“PCC”,又名“悲觀鎖”】。

悲觀鎖,具有強(qiáng)烈的獨(dú)占和排他特性。它指的是對(duì)數(shù)據(jù)被外界(包括本系統(tǒng)當(dāng)前的其他事務(wù),以及來(lái)自外部系統(tǒng)的事務(wù)處理)修改持保守態(tài)度。因此,在整個(gè)數(shù)據(jù)處理過(guò)程中,將數(shù)據(jù)處于鎖定狀態(tài)。悲觀鎖的實(shí)現(xiàn),往往依靠數(shù)據(jù)庫(kù)提供的鎖機(jī)制(也只有數(shù)據(jù)庫(kù)層提供的鎖機(jī)制才能真正保證數(shù)據(jù)訪問(wèn)的排他性,否則,即使在本系統(tǒng)中實(shí)現(xiàn)了加鎖機(jī)制,也無(wú)法保證外部系統(tǒng)不會(huì)修改數(shù)據(jù))

之所以叫做悲觀鎖,是因?yàn)檫@是一種對(duì)數(shù)據(jù)的修改持有悲觀態(tài)度的并發(fā)控制方式??偸羌僭O(shè)最壞的情況,每次讀取數(shù)據(jù)的時(shí)候都默認(rèn)其他線程會(huì)更改數(shù)據(jù),因此需要進(jìn)行加鎖操作,當(dāng)其他線程想要訪問(wèn)數(shù)據(jù)時(shí),都需要阻塞掛起。悲觀鎖的實(shí)現(xiàn):

  1. 傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)使用這種鎖機(jī)制,比如行鎖、表鎖、讀鎖、寫(xiě)鎖等,都是在操作之前先上鎖。
  2. Java 里面的同步 synchronized 關(guān)鍵字的實(shí)現(xiàn)。

2??悲觀鎖主要分為共享鎖和排他鎖

  • 共享鎖【shared locks】又稱為讀鎖,簡(jiǎn)稱 S 鎖。顧名思義,共享鎖就是多個(gè)事務(wù)對(duì)于同一數(shù)據(jù)可以共享一把鎖,都能訪問(wèn)到數(shù)據(jù),但是只能讀不能修改。
  • 排他鎖【exclusive locks】又稱為寫(xiě)鎖,簡(jiǎn)稱 X 鎖。顧名思義,排他鎖就是不能與其他鎖并存,如果一個(gè)事務(wù)獲取了一個(gè)數(shù)據(jù)行的排他鎖,其他事務(wù)就不能再獲取該行的其他鎖,包括共享鎖和排他鎖。獲取排他鎖的事務(wù)可以對(duì)數(shù)據(jù)行讀取和修改。

3??說(shuō)明
悲觀并發(fā)控制實(shí)際上是“先取鎖再訪問(wèn)”的保守策略,為數(shù)據(jù)處理的安全提供了保證。但是在效率方面,處理加鎖的機(jī)制會(huì)讓數(shù)據(jù)庫(kù)產(chǎn)生額外的開(kāi)銷,還有增加產(chǎn)生死鎖的機(jī)會(huì)。另外還會(huì)降低并行性,一個(gè)事務(wù)如果鎖定了某行數(shù)據(jù),其他事務(wù)就必須等待該事務(wù)處理完才可以處理那行數(shù)據(jù)。

三、樂(lè)觀鎖(Optimistic Locking)

1??理解
樂(lè)觀鎖是相對(duì)悲觀鎖而言的,樂(lè)觀鎖假設(shè)數(shù)據(jù)一般情況不會(huì)造成沖突,所以在數(shù)據(jù)進(jìn)行提交更新的時(shí)候,才會(huì)正式對(duì)數(shù)據(jù)的沖突與否進(jìn)行檢測(cè),如果沖突,則返回給用戶異常信息,讓用戶決定如何去做。樂(lè)觀鎖適用于讀多寫(xiě)少的場(chǎng)景,這樣可以提高程序的吞吐量。

樂(lè)觀鎖采取了更加寬松的加鎖機(jī)制。也是為了避免數(shù)據(jù)庫(kù)幻讀、業(yè)務(wù)處理時(shí)間過(guò)長(zhǎng)等原因引起數(shù)據(jù)處理錯(cuò)誤的一種機(jī)制,但樂(lè)觀鎖不會(huì)刻意使用數(shù)據(jù)庫(kù)本身的鎖機(jī)制,而是依據(jù)數(shù)據(jù)本身來(lái)保證數(shù)據(jù)的正確性。樂(lè)觀鎖的實(shí)現(xiàn):

  1. CAS 實(shí)現(xiàn):Java 中java.util.concurrent.atomic包下面的原子變量使用了樂(lè)觀鎖的一種 CAS 實(shí)現(xiàn)方式。
  2. 版本號(hào)控制:一般是在數(shù)據(jù)表中加上一個(gè)數(shù)據(jù)版本號(hào) version 字段,表示數(shù)據(jù)被修改的次數(shù)。當(dāng)數(shù)據(jù)被修改時(shí),version 值會(huì) +1。當(dāng)線程 A 要更新數(shù)據(jù)時(shí),在讀取數(shù)據(jù)的同時(shí)也會(huì)讀取 version 值,在提交更新時(shí),若剛才讀取到的 version 值與當(dāng)前數(shù)據(jù)庫(kù)中的 version 值相等時(shí)才更新,否則重試更新操作,直到更新成功。

2??說(shuō)明
樂(lè)觀并發(fā)控制相信事務(wù)之間的數(shù)據(jù)競(jìng)爭(zhēng)(data race)的概率是比較小的,因此盡可能直接做下去,直到提交的時(shí)候才去鎖定,所以不會(huì)產(chǎn)生任何鎖和死鎖。

四、具體實(shí)現(xiàn)

1??悲觀鎖實(shí)現(xiàn)方式
悲觀鎖的實(shí)現(xiàn),往往依靠數(shù)據(jù)庫(kù)提供的鎖機(jī)制。在數(shù)據(jù)庫(kù)中,悲觀鎖的流程如下:

  1. 在對(duì)記錄進(jìn)行修改前,先嘗試為該記錄加上排他鎖(exclusive locks)。
  2. 如果加鎖失敗,說(shuō)明該記錄正在被修改,那么當(dāng)前查詢可能要等待或者拋出異常。具體響應(yīng)方式由開(kāi)發(fā)者根據(jù)實(shí)際需要決定。
  3. 如果成功加鎖,那么就可以對(duì)記錄做修改,事務(wù)完成后就會(huì)解鎖了。
  4. 期間如果有其他對(duì)該記錄做修改或加排他鎖的操作,都會(huì)等待解鎖或直接拋出異常。

MySql Innodb 引擎舉例,說(shuō)明 SQL 中悲觀鎖的應(yīng)用

要使用悲觀鎖,必須關(guān)閉 MySQL 數(shù)據(jù)庫(kù)的自動(dòng)提交屬性set autocommit=0。因?yàn)?MySQL 默認(rèn)使用 autocommit 模式,也就是說(shuō),當(dāng)執(zhí)行一個(gè)更新操作后,MySQL 會(huì)立刻將結(jié)果進(jìn)行提交。

以電商下單扣減庫(kù)存的過(guò)程說(shuō)明一下悲觀鎖的使用:

在對(duì) id = 1 的記錄修改前,先通過(guò) for update 的方式進(jìn)行加鎖,然后再進(jìn)行修改。這就是比較典型的悲觀鎖策略。

如果發(fā)生并發(fā),同一時(shí)間只有一個(gè)線程可以開(kāi)啟事務(wù)并獲得 id=1 的鎖,其它的事務(wù)必須等本次事務(wù)提交之后才能執(zhí)行。這樣可以保證當(dāng)前的數(shù)據(jù)不會(huì)被其它事務(wù)修改。

使用 select…for update 鎖數(shù)據(jù),需要注意鎖的級(jí)別,MySQL InnoDB 默認(rèn)行級(jí)鎖。行級(jí)鎖都是基于索引的,如果一條 SQL 語(yǔ)句用不到索引是不會(huì)使用行級(jí)鎖的,會(huì)使用表級(jí)鎖把整張表鎖住,這點(diǎn)需要注意。

2??樂(lè)觀鎖實(shí)現(xiàn)方式樂(lè)觀鎖不需要借助數(shù)據(jù)庫(kù)的鎖機(jī)制

主要就是兩個(gè)步驟:沖突檢測(cè)和數(shù)據(jù)更新。比較典型的就是 CAS (Compare and Swap)。

CAS 即比較并交換。是解決多線程并行情況下使用鎖造成性能損耗的一種機(jī)制,CAS 操作包含三個(gè)操作數(shù)——內(nèi)存位置(V)、預(yù)期原值(A)和新值(B)。如果內(nèi)存位置的值(V)與預(yù)期原值(A)相匹配,那么處理器會(huì)自動(dòng)將該位置值更新為新值(B)。否則,處理器不做任何操作。無(wú)論哪種情況,它都會(huì)在 CAS 指令之前返回該位置的值。CAS 有效地說(shuō)明了“我認(rèn)為位置(V)應(yīng)該包含值(A)。如果包含該值,則將新值(B)放到這個(gè)位置;否則,不要更改該位置,只告訴我這個(gè)位置現(xiàn)在的值即可”。Java 中,sun.misc.Unsafe 類提供了硬件級(jí)別的原子操作來(lái)實(shí)現(xiàn)這個(gè) CAS。java.util.concurrent包下大量的類都使用了這個(gè) Unsafe.java 類的 CAS 操作。

當(dāng)多個(gè)線程嘗試使用 CAS 同時(shí)更新同一個(gè)變量時(shí),只有其中一個(gè)線程能更新變量的值,而其它線程都失敗,失敗的線程并不會(huì)被掛起,而是被告知這次競(jìng)爭(zhēng)中失敗,并可以再次嘗試。比如前面的扣減庫(kù)存問(wèn)題,通過(guò)樂(lè)觀鎖可以實(shí)現(xiàn)如下:
樂(lè)觀鎖使用

在更新之前,先查詢一下庫(kù)存表中當(dāng)前庫(kù)存數(shù)(quantity),然后在做 update 的時(shí)候,以庫(kù)存數(shù)作為一個(gè)修改條件。當(dāng)提交更新的時(shí)候,判斷數(shù)據(jù)庫(kù)表對(duì)應(yīng)記錄的當(dāng)前庫(kù)存數(shù)與第一次取出來(lái)的庫(kù)存數(shù)進(jìn)行比對(duì),如果數(shù)據(jù)庫(kù)表當(dāng)前庫(kù)存數(shù)與第一次取出來(lái)的庫(kù)存數(shù)相等,則予以更新,否則認(rèn)為是過(guò)期數(shù)據(jù)。

以上更新語(yǔ)句存在一個(gè)比較嚴(yán)重的問(wèn)題,即ABA問(wèn)題

  1. 比如說(shuō)線程一從數(shù)據(jù)庫(kù)中取出庫(kù)存數(shù) 3,這時(shí)候線程二也從數(shù)據(jù)庫(kù)中取出庫(kù)存數(shù) 3,并且線程二進(jìn)行了一些操作變成了 2。
  2. 然后線程二又將庫(kù)存數(shù)變成 3,這時(shí)候線程一進(jìn)行 CAS 操作發(fā)現(xiàn)數(shù)據(jù)庫(kù)中仍然是 3,然后線程一操作成功。
  3. 盡管線程一的 CAS 操作成功,但是不代表這個(gè)過(guò)程就是沒(méi)有問(wèn)題的。

一個(gè)比較好的解決辦法,就是通過(guò)一個(gè)單獨(dú)的可以順序遞增的 version 字段。優(yōu)化如下:

樂(lè)觀鎖每次在執(zhí)行數(shù)據(jù)修改操作時(shí),都會(huì)帶上一個(gè)版本號(hào),一旦版本號(hào)和數(shù)據(jù)的版本號(hào)一致就可以執(zhí)行修改操作并對(duì)版本號(hào)執(zhí)行 +1 操作,否則就執(zhí)行失敗。因?yàn)槊看尾僮鞯陌姹咎?hào)都會(huì)隨之增加,所以不會(huì)出現(xiàn) ABA 問(wèn)題。除了 version 以外,還可以使用時(shí)間戳,因?yàn)闀r(shí)間戳天然具有順序遞增性。

以上 SQL 其實(shí)還是有一定的問(wèn)題的,就是一旦遇上高并發(fā)的時(shí)候,就只有一個(gè)線程可以修改成功,那么就會(huì)存在大量的失敗。對(duì)于像淘寶這樣的電商網(wǎng)站,高并發(fā)是常有的事,總讓用戶感知到失敗顯然是不合理的。所以,還是要想辦法減少樂(lè)觀鎖的粒度。一個(gè)比較好的建議,就是減小樂(lè)觀鎖力度,最大程度的提升吞吐率,提高并發(fā)能力!如下:

以上 SQL 語(yǔ)句中,如果用戶下單數(shù)為 1,則通過(guò)quantity - 1 > 0的方式進(jìn)行樂(lè)觀鎖控制。在執(zhí)行過(guò)程中,會(huì)在一次原子操作中查詢一遍 quantity 的值,并將其扣減掉 1。

高并發(fā)環(huán)境下鎖粒度把控是一門(mén)重要的學(xué)問(wèn)。選擇一個(gè)好的鎖,在保證數(shù)據(jù)安全的情況下,可以大大提升吞吐率,進(jìn)而提升性能。

五、理解 CAS 底層

流程圖

假如說(shuō)有 3 個(gè)線程并發(fā)的要修改一個(gè) AtomicInteger 的值,底層機(jī)制如下:

  1. 首先,每個(gè)線程都會(huì)先獲取當(dāng)前的值,接著走一個(gè)原子的 CAS 操作。原子的意思就是這個(gè) CAS 操作一定是自己完整執(zhí)行完的,不會(huì)被別人打斷。
  2. 然后 CAS 操作里,會(huì)比較一下,現(xiàn)在的值是不是剛才獲取到的那個(gè)值。如果是,說(shuō)明沒(méi)人改過(guò)這個(gè)值,然后設(shè)置成累加 1 之后的一個(gè)值。
  3. 同理,如果有人在執(zhí)行 CAS 的時(shí)候,發(fā)現(xiàn)之前獲取的值跟當(dāng)前的值不一樣,會(huì)導(dǎo)致 CAS 失敗。失敗之后,進(jìn)入一個(gè)無(wú)限循環(huán),再次獲取值,接著執(zhí)行 CAS 操作。

六、CAS 典型應(yīng)用

java.util.concurrent.atomic包下的類大多是使用 CAS 操作來(lái)實(shí)現(xiàn)的,比如 AtomicInteger、AtomicBoolean、AtomicLong。一般在競(jìng)爭(zhēng)不是特別激烈的時(shí)候,使用該包下的原子操作性能比使用 synchronized 關(guān)鍵字的方式高效的多(查看 getAndSet(),可知如果資源競(jìng)爭(zhēng)十分激烈的話,這個(gè) for 循環(huán)可能會(huì)持續(xù)很久都不能成功跳出。不過(guò)這種情況可能需要考慮降低資源競(jìng)爭(zhēng)才是)。
在較多的場(chǎng)景都可能會(huì)使用到這些原子類操作。一個(gè)典型應(yīng)用就是計(jì)數(shù)了,在多線程的情況下需要考慮線程安全問(wèn)題。

1??支持計(jì)數(shù)功能 Demo 實(shí)現(xiàn)

public class Increment {
    private int count = 0;
    public void add() {
        count++;
    }
}

在并發(fā)環(huán)境下對(duì) count 進(jìn)行自增運(yùn)算是不安全的,為什么不安全以及如何解決這個(gè)問(wèn)題呢?

2??為什么并發(fā)環(huán)境下的 count 自增操作不安全?因?yàn)?count++ 不是原子操作,而是三個(gè)原子操作的組合:

  1. 讀取內(nèi)存中的 count 值賦值給局部變量 temp;
  2. 執(zhí)行 temp+1 操作;
  3. 將 temp 賦值給 count。

所以如果兩個(gè)線程同時(shí)執(zhí)行 count++ 的話,不能保證線程一按順序執(zhí)行完上述三步后線程二才開(kāi)始執(zhí)行。

3??并發(fā)環(huán)境下 count++ 不安全問(wèn)題的解決方案

方案①:synchronized 加鎖。同一時(shí)間只有一個(gè)線程能加鎖,其他線程需要等待鎖,這樣就不會(huì)出現(xiàn) count 計(jì)數(shù)不準(zhǔn)確的問(wèn)題了:

public class Increment {
    private int count = 0;
    public synchronized void add() {
        count++;
    }
}

但是引入 synchronized 會(huì)造成多個(gè)線程排隊(duì)的問(wèn)題,相當(dāng)于讓各個(gè)線程串行化了,一個(gè)接一個(gè)的排隊(duì)、加鎖、處理數(shù)據(jù)、釋放鎖,下一個(gè)再進(jìn)來(lái)。同一時(shí)間只有一個(gè)線程執(zhí)行,這樣的鎖有點(diǎn)“重量級(jí)”了。這類似于悲觀鎖的實(shí)現(xiàn),需要獲取這個(gè)資源,就給它加鎖,別的線程都無(wú)法訪問(wèn)該資源,直到操作完后釋放對(duì)該資源的鎖。雖然隨著 Java 版本更新,也對(duì) synchronized 做了很多優(yōu)化,但是處理這種簡(jiǎn)單的累加操作,仍然顯得“太重了”。

方案②:Atomic 原子類。對(duì)于 count++ 的操作,完全可以換一種做法,Java 并發(fā)包下面提供了一系列的 Atomic 原子類,比如說(shuō) AtomicInteger:

//import java.util.concurrent.atomic.AtomicInteger;
public static void main(String[] args) {
    public static AtomicInteger count = new AtomicInteger(0);
    public static void increase() {
        count.incrementAndGet();
    }
}

多個(gè)線程可以并發(fā)的執(zhí)行 AtomicInteger 的 incrementAndGet(),意思就是把 count 的值累加 1,接著返回累加后最新的值。實(shí)際上,Atomic 原子類底層用的不是傳統(tǒng)意義的鎖機(jī)制,而是無(wú)鎖化的 CAS 機(jī)制,通過(guò) CAS 機(jī)制保證多線程修改一個(gè)數(shù)值的安全性。

七、CAS 性能優(yōu)化

從流程圖可以看出來(lái),大量的線程同時(shí)并發(fā)修改一個(gè) AtomicInteger,可能有很多線程會(huì)不停的自旋,進(jìn)入一個(gè)無(wú)限重復(fù)的循環(huán)中。這些線程不停地獲取值,然后發(fā)起 CAS 操作,但是發(fā)現(xiàn)這個(gè)值被別人改過(guò)了,于是再次進(jìn)入下一個(gè)循環(huán),獲取值,發(fā)起 CAS 操作又失敗了,再次進(jìn)入下一個(gè)循環(huán)。在大量線程高并發(fā)更新 AtomicInteger 的時(shí)候,這種問(wèn)題可能會(huì)比較明顯,導(dǎo)致大量線程空循環(huán),自旋轉(zhuǎn),性能和效率都不是特別好。那么如何優(yōu)化呢?

Java8 有一個(gè)新的類,LongAdder,它就是嘗試使用分段 CAS 以及自動(dòng)分段遷移的方式來(lái)大幅度提升多線程高并發(fā)執(zhí)行 CAS 操作的性能,這個(gè)類具體是如何優(yōu)化性能的呢?如圖:

LongAdder

LongAdder 核心思想就是熱點(diǎn)分離,這一點(diǎn)和 ConcurrentHashMap 的設(shè)計(jì)思想相似。就是將 value 值分離成一個(gè)數(shù)組,當(dāng)多線程訪問(wèn)時(shí),通過(guò) hash 算法映射到其中的一個(gè)數(shù)字進(jìn)行計(jì)數(shù)。而最終的結(jié)果,就是這些數(shù)組的求和累加。這樣一來(lái),就減小了鎖的粒度。

LongAddr 的兄弟類如下:
LongAdder兄弟類

八、如何選擇

在樂(lè)觀鎖與悲觀鎖的選擇上面,主要看下兩者的區(qū)別以及適用場(chǎng)景就可以了。
1??響應(yīng)效率:如果需要非常高的響應(yīng)速度,建議采用樂(lè)觀鎖方案,成功就執(zhí)行,不成功就失敗,不需要等待其他并發(fā)去釋放鎖。樂(lè)觀鎖并未真正加鎖,效率高。一旦鎖的粒度掌握不好,更新失敗的概率就會(huì)比較高,容易發(fā)生業(yè)務(wù)失敗。
2??沖突頻率:如果沖突頻率非常高,建議采用悲觀鎖,保證成功率。沖突頻率大,選擇樂(lè)觀鎖會(huì)需要多次重試才能成功,代價(jià)比較大。
3??重試代價(jià):如果重試代價(jià)大,建議采用悲觀鎖。悲觀鎖依賴數(shù)據(jù)庫(kù)鎖,效率低。更新失敗的概率比較低。
4??樂(lè)觀鎖如果有人在你之前更新了,你的更新應(yīng)當(dāng)是被拒絕的,可以讓用戶從新操作。悲觀鎖則會(huì)等待前一個(gè)更新完成。這也是區(qū)別。

隨著互聯(lián)網(wǎng)三高架構(gòu)(高并發(fā)、高性能、高可用)的提出,悲觀鎖已經(jīng)越來(lái)越少的被應(yīng)用到生產(chǎn)環(huán)境中了,尤其是并發(fā)量比較大的業(yè)務(wù)場(chǎng)景。

MySQL樂(lè)觀鎖電商庫(kù)存并發(fā)問(wèn)題應(yīng)用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 簡(jiǎn)單總結(jié):悲觀鎖是線程訪問(wèn)的時(shí)候鎖住直接鎖住訪問(wèn)數(shù)據(jù),java里面的synchronized就是一種悲觀鎖。樂(lè)觀鎖...
    salix_閱讀 489評(píng)論 0 0
  • 問(wèn)題引入 當(dāng)程序中可能出現(xiàn)并發(fā)的情況時(shí),我們就需要通過(guò)一定的手段來(lái)保證在并發(fā)情況下數(shù)據(jù)的準(zhǔn)確性,通過(guò)這種手段保證了...
    njitzyd閱讀 258評(píng)論 0 1
  • 前言 在數(shù)據(jù)庫(kù)的實(shí)際使用過(guò)程中,我們常常會(huì)遇到不希望數(shù)據(jù)被同時(shí)寫(xiě)或者讀的情景,例如秒殺場(chǎng)景下,兩個(gè)請(qǐng)求同時(shí)讀到系統(tǒng)...
    X先生說(shuō)閱讀 876評(píng)論 0 0
  • 一、前言 在了解悲觀鎖和樂(lè)觀鎖之前,我們先了解一下什么是鎖,為什么要用到鎖? 技術(shù)來(lái)源于生活,鎖不僅在程序中存在,...
    牧小農(nóng)閱讀 687評(píng)論 0 1
  • 久違的晴天,家長(zhǎng)會(huì)。 家長(zhǎng)大會(huì)開(kāi)好到教室時(shí),離放學(xué)已經(jīng)沒(méi)多少時(shí)間了。班主任說(shuō)已經(jīng)安排了三個(gè)家長(zhǎng)分享經(jīng)驗(yàn)。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,788評(píng)論 16 22

友情鏈接更多精彩內(nèi)容