摘要:加鎖才能保證線程安全使用之后,不加鎖,也是線程安全的。確保不出現(xiàn)線程安全問(wèn)題。一般在數(shù)據(jù)庫(kù)中使用樂(lè)觀鎖都會(huì)拿版本號(hào)作為對(duì)比值,因?yàn)榘姹咎?hào)會(huì)一直增加,沒(méi)有重復(fù)的,所以不會(huì)出現(xiàn)這個(gè)問(wèn)題。
悲觀鎖:
認(rèn)為每次獲取數(shù)據(jù)的時(shí)候數(shù)據(jù)一定會(huì)被人修改,所以它在獲取數(shù)據(jù)的時(shí)候會(huì)把操作的數(shù)據(jù)給鎖住,這樣一來(lái)就只有它自己能夠操作,其他人都堵塞在那里。
樂(lè)觀鎖:認(rèn)為每次獲取數(shù)據(jù)的時(shí)候數(shù)據(jù)不會(huì)被別人修改,所以獲取數(shù)據(jù)的時(shí)候并沒(méi)有鎖住整個(gè)數(shù)據(jù),但是在更新的時(shí)候它會(huì)去判斷一下要更新的數(shù)據(jù)有沒(méi)有被別人修改過(guò),例如更新前查詢?cè)摂?shù)據(jù)的版本號(hào),更新的時(shí)候看看該版本號(hào)有沒(méi)有被人修改過(guò),如果被人修改過(guò)了,那就不會(huì)去更新。
應(yīng)用場(chǎng)景:悲觀鎖:
因?yàn)楸^鎖會(huì)鎖住數(shù)據(jù),讓其他人都等待,所以當(dāng)一個(gè)系統(tǒng)并發(fā)量不大,而且可以接收一定延遲的時(shí)候可以選擇悲觀鎖。
樂(lè)觀鎖:
因?yàn)闃?lè)觀鎖會(huì)在更新前去查數(shù)據(jù),所以比較適合讀多少寫(xiě)的場(chǎng)景,因?yàn)閷?xiě)操作多的話會(huì)造成大量的查詢操作,給系統(tǒng)帶來(lái)壓力。例如SVN、Git等版本控制管理器就是應(yīng)用的樂(lè)觀鎖,當(dāng)你提交數(shù)據(jù)的時(shí)候?qū)Ρ认掳姹咎?hào),如果遠(yuǎn)程倉(cāng)庫(kù)的版本號(hào)和本地的不一樣就表示有人已經(jīng)提交過(guò)代碼了,你需要先更新代碼到本地處理一下版本沖突問(wèn)題,不然是沒(méi)有辦法提交的。
CAS是Compare And Set的縮寫(xiě),中文意思就是比較和操作,是一個(gè)非阻塞算法。它其實(shí)是一個(gè)CPU的指令,它會(huì)拿內(nèi)存值和一個(gè)給定的值進(jìn)行比較,如果相等的話就會(huì)把內(nèi)存值更新為另一個(gè)給定的值。其實(shí)CAS就是使用一個(gè)樂(lè)觀鎖的機(jī)制。
Java中CAS機(jī)制的應(yīng)用:從JDK1.5開(kāi)始java.util.concurrent.atomic包中新增了一些原子類,AtomicInteger、AtomicLong等等,就是專門(mén)解決高并發(fā)下的同步問(wèn)題。因?yàn)轭愃苅++、++i的操作不是線程安全的,以前我們都會(huì)使用Synchronized關(guān)鍵字,但是現(xiàn)在我們直接使用這些原子類就可以解決線程安全的問(wèn)題。下面用代碼來(lái)看看有什么變化。
class Test1 { private volatile int count = 0; public synchronized void increment() { //加鎖才能保證線程安全 count++; } public int getCount() { return count; } } class Test2 { private AtomicInteger count = new AtomicInteger(); //使用AtomicInteger之后,不加鎖,也是線程安全的。 public void increment() { count.incrementAndGet(); } public int getCount() { return count.get(); } }
下面這些是AtomicInteger提供的別的方法。
//獲取當(dāng)前的值 public final int get() //獲取當(dāng)前的值,并設(shè)置新的值 public final int getAndSet(int newValue) //獲取當(dāng)前的值,并自增 public final int getAndIncrement() //獲取當(dāng)前的值,并自減 public final int getAndDecrement() //獲取當(dāng)前的值,并加上預(yù)期的值 public final int getAndAdd(int delta)
我們從源碼的角度看看AtomicInteger是怎么實(shí)現(xiàn)CAS機(jī)制的。unsafe是java提供的用來(lái)獲取對(duì)象內(nèi)存地址的類,作用是在更新操作時(shí)提供“比較并替換”的作用。valueOffset是記錄value本身在內(nèi)存的地址,value被聲明為volatile是保證在更新操作時(shí),當(dāng)前線程可以拿到value最新的值。
public class AtomicInteger extends Number implements java.io.Serializable { private static final long serialVersionUID = 6214790243416807050L; // setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } private volatile int value;
比如incrementAndGet方法,是獲取當(dāng)前的值并自增。
public final int incrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, 1) + 1; }
我們進(jìn)getAndAddInt方法看看,getIntVolatile和compareAndSwapInt都是本地方法,就是通過(guò)本地方法來(lái)實(shí)現(xiàn)CAS機(jī)制。確保不出現(xiàn)線程安全問(wèn)題。
public final int getAndSetInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var4)); return var5; } public native int getIntVolatile(Object var1, long var2); public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);CAS可能會(huì)引起的問(wèn)題
因?yàn)镃AS并不會(huì)鎖住數(shù)據(jù)讓其他線程阻塞,所以實(shí)際上是自旋鎖的原理。自旋鎖就是當(dāng)線程獲取鎖的時(shí)候發(fā)現(xiàn)這個(gè)鎖已經(jīng)被別的線程搶了,它不是阻塞自己,而是一直循環(huán)查看這個(gè)鎖有沒(méi)有被釋放,這就叫自旋鎖。因?yàn)橐恢毖h(huán)查看所以可以能會(huì)造成CPU負(fù)擔(dān)過(guò)重,最好設(shè)置參數(shù)限制查看鎖的次數(shù)。
死鎖問(wèn)題,有一個(gè)線程拿到自旋鎖之后,又去拿鎖,例如遞歸的時(shí)候會(huì)出現(xiàn)這樣的情況,自己等待自己釋放縮,卡在那里不動(dòng)。
ABA問(wèn)題,這個(gè)問(wèn)題就是說(shuō)當(dāng)線程1讀到內(nèi)存值為A,然后線程2進(jìn)來(lái)了把內(nèi)存值改為B,然后又改為了A,這個(gè)時(shí)候線程1覺(jué)得沒(méi)有問(wèn)題,就更新了。一般在數(shù)據(jù)庫(kù)中使用樂(lè)觀鎖都會(huì)拿版本號(hào)作為對(duì)比值,因?yàn)榘姹咎?hào)會(huì)一直增加,沒(méi)有重復(fù)的,所以不會(huì)出現(xiàn)這個(gè)問(wèn)題。Java中也提供了AtomicStampedReference這個(gè)類,大致原理也是提供一個(gè)版本號(hào)來(lái)對(duì)比。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69110.html
摘要:本文旨在對(duì)鎖相關(guān)源碼本文中的源碼來(lái)自使用場(chǎng)景進(jìn)行舉例,為讀者介紹主流鎖的知識(shí)點(diǎn),以及不同的鎖的適用場(chǎng)景。中,關(guān)鍵字和的實(shí)現(xiàn)類都是悲觀鎖。自適應(yīng)意味著自旋的時(shí)間次數(shù)不再固定,而是由前一次在同一個(gè)鎖上的自旋時(shí)間及鎖的擁有者的狀態(tài)來(lái)決定。 前言 Java提供了種類豐富的鎖,每種鎖因其特性的不同,在適當(dāng)?shù)膱?chǎng)景下能夠展現(xiàn)出非常高的效率。本文旨在對(duì)鎖相關(guān)源碼(本文中的源碼來(lái)自JDK 8)、使用場(chǎng)景...
摘要:算法算法會(huì)先對(duì)一個(gè)內(nèi)存變量位置和一個(gè)給定的值進(jìn)行比較,如果相等,則用一個(gè)新值去修改這個(gè)內(nèi)存變量位置。因?yàn)槭欠枪芥i,所以一上來(lái)就嘗試搶占鎖給定舊值并希望用新值去更新內(nèi)存變量。 本文翻譯和原創(chuàng)各占一半,所以還是厚顏無(wú)恥歸類到原創(chuàng)好了...https://howtodoinjava.com/jav...java 5 其中一個(gè)令人振奮的改進(jìn)是新增了支持原子操作的類型,例如 AtomicInt...
摘要:有可能,會(huì)造成優(yōu)先級(jí)反轉(zhuǎn)或者饑餓現(xiàn)象。悲觀鎖在中的使用,就是利用各種鎖。對(duì)于而言,其是獨(dú)享鎖。偏向鎖,顧名思義,它會(huì)偏向于第一個(gè)訪問(wèn)鎖的線程,大多數(shù)情況下鎖不僅不存在多線程競(jìng)爭(zhēng),而且總是由同一線程多次獲得。 理解鎖的基礎(chǔ)知識(shí) 如果想要透徹的理解java鎖的來(lái)龍去脈,需要先了解以下基礎(chǔ)知識(shí)。 基礎(chǔ)知識(shí)之一:鎖的類型 按照其性質(zhì)分類 公平鎖/非公平鎖 公平鎖是指多個(gè)線程按照申請(qǐng)鎖的順序來(lái)獲...
摘要:所以悲觀鎖是限制其他線程,而樂(lè)觀鎖是限制自己,雖然他的名字有鎖,但是實(shí)際上不算上鎖,只是在最后操作的時(shí)候再判斷具體怎么操作。悲觀鎖和樂(lè)觀鎖比較悲觀鎖適合寫(xiě)多讀少的場(chǎng)景。 最近在公司的業(yè)務(wù)上遇到了并發(fā)的問(wèn)題,并且還是很常見(jiàn)的并發(fā)問(wèn)題,算是低級(jí)的失誤了。由于公司業(yè)務(wù)相對(duì)比較復(fù)雜且不適合公開(kāi),在此用一個(gè)很常見(jiàn)的業(yè)務(wù)來(lái)還原一下場(chǎng)景,同時(shí)介紹悲觀鎖和樂(lè)觀鎖是如何解決這類并發(fā)問(wèn)題的。 公司業(yè)務(wù)就是...
閱讀 1780·2021-11-11 16:55
閱讀 2579·2021-08-27 13:11
閱讀 3641·2019-08-30 15:53
閱讀 2314·2019-08-30 15:44
閱讀 1402·2019-08-30 11:20
閱讀 1049·2019-08-30 10:55
閱讀 953·2019-08-29 18:40
閱讀 3048·2019-08-29 16:13